Table of Contents

Next Page

Professional ASP.NET 2.0 AJAX
Professional ASP.NET 2.0 AJAX
byMatt GibbsandDan Wahlin
Wrox Press 2007 (327 pages)
ISBN:9780470109625

Written by Microsoft's lead ASP.NET AJAX developer together with an in-the-field Microsoft MVP, this book guides you through the core of the Microsoft AJAX Library and the controls you will use to leverage AJAX.

Table of Contents
 | Professional ASP.NET 2.0 AJAX
 | Credits
 | Introduction
 | Chapter 1 | - | Developing Next-Generation Web Applications
 | Chapter 2 | - | Partial Page Updates
 | Chapter 3 | - | JavaScript for the ASP.NET Developer
 | Chapter 4 | - | Understanding the ASP.NET AJAX Client Library
 | Chapter 5 | - | Using the ScriptManager
 | Chapter 6 | - | ASP.NET AJAX Networking
 | Chapter 7 | - | ASP.NET AJAX Application Services
 | Chapter 8 | - | Rich AJAX Toolkit Controls
 | Chapter 9 | - | Testing and Debugging ASP.NET AJAX Applications
 | Chapter 10 | - | Deploying ASP.NET AJAX Applications
 | Chapter 11 | - | Building Custom Controls
 | Index
 | List of Sidebars

Next Page


Back Cover

Previous Page
Next Page

Back Cover

Are you ready to build more responsive web applications with richer UI elements? This detailed guide to the Microsoft AJAX Library shows you how to bring together ASP.NET 2.0 with an object-oriented approach to JavaScript development using this new framework from Microsoft. You'll gain an in-depth understanding of the ASP.NET AJAX architecture, including the server controls, client-side JavaScript library, and runtime environment. By following the numerous examples, you'll see how much your users benefit from this new class of web applications that manipulate the browser's DOM and communicate with the server to update the user's view of a page without waiting for the entire page to be refreshed.

Written by Microsoft's lead ASP.NET AJAX developer together with an in-the-field Microsoft MVP (Most Valuable Professional), this book guides you through the core of the Microsoft AJAX Library and the controls you will use to leverage AJAX. It provides you with code samples to take advantage of some of the popular shared-source ASP.NET AJAX Toolkit controls and extensions. And you'll work through debugging functionality in Visual Studio and the browser as well as methods for effective testing.

What you will learn from this book

  • How to create a better user experience by adding more dynamic UIs
  • Steps for accessing ASP.NET profile and authentication services
  • Ways to simplify programming tasks using the Microsoft AJAX Library
  • Strategies for testing web applications to achieve optimal performance
  • Methods for developing custom AJAX controls

Who this book is for

This book is for experienced ASP.NET developers who want to add AJAX to their applications. It is also for experienced web developers who want to move to ASP.NET and AJAX together.

About the Authors

Matt Gibbs is currently the development manager of the UI Framework and Services Team. This talented group is responsible for ASP.NET and the AJAX Framework as well as the new Integrated Pipeline of IIS 7. Matt has been working on Microsoft web technologies since joining the IIS 4 team to work on “classic” ASP in 1997. He has co-authored several books on ASP and ASP.NET.

Dan Wahlin (Microsoft Most Valuable Professional for ASP.NET and XML Web Services) is a .NET development instructor at Interface Technical Training. Dan founded the XML for ASP.NET Developers Website (www.XMLforASP.NET), which focuses on using XML, ADO.NET, and Web Services in Microsoft’s .NET platform. He’s also on the INETA Speakers Bureau and speaks at several conferences. Dan has co-authored/authored several different books on .NET, including ASP.NET 2.0 MVP Hacks and XML for ASP.NET Developers.

Previous Page


Professional ASP.NET 2.0 AJAX

Matt Gibbs

and

Dan Wahlin
Image from book
Wiley Publishing. Inc.

Published by

Wiley Publishing, Inc.
10475 Crosspoint Boulevard
Indianapolis, IN 46256
www.wiley.com

© 2007 by Wiley Publishing, Inc., Indianapolis, Indiana

Published simultaneously in Canada

ISBN: 9780470109625

Manufactured in the United States of America

10 9 8 7 6 5 4 3 2 1

Library of Congress Cataloging-in-Publication Data:

Gibbs, Matthew, 1971-
Professional ASP.NET 2.0 Ajax / Matt Gibbs and Dan Wahlin.
p. cm.
Includes index.
ISBN 978-0-470-10962-5 (paper/website)
1. Active server pages. 2. Web sites–Design. 3. Microsoft .NET. I. Wahlin, Dan. II. Title.
TK5105.8885.A26G532 2007
006.7’6–dc22
2007014621

No part of this publication may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, scanning, or otherwise, except as permitted under Sections 107 or 108 of the 1976 United States Copyright Act, without either the prior written permission of the Publisher, or authorization through payment of the appropriate per-copy fee to the Copyright Clearance Center, 222 Rosewood Drive, Danvers, MA 01923, (978) 750-8400, fax (978) 646-8600. Requests to the Publisher for permission should be addressed to the Legal Department, Wiley Publishing, Inc., 10475 Crosspoint Blvd., Indianapolis, IN 46256, (317) 572-3447, fax (317) 572-4355, or online at http://www.wiley.com/go/permissions.

LIMIT OF LIABILITY/DISCLAIMER OF WARRANTY: THE PUBLISHER AND THE AUTHOR MAKE NO REPRESENTATIONS OR WARRANTIES WITH RESPECT TO THE ACCURACY OR COMPLETENESS OF THE CONTENTS OF THIS WORK AND SPECIFICALLY DISCLAIM ALL WARRANTIES, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE. NO WARRANTY MAY BE CREATED OR EXTENDED BY SALES OR PROMOTIONAL MATERIALS. THE ADVICE AND STRATEGIES CONTAINED HEREIN MAY NOT BE SUITABLE FOR EVERY SITUATION. THIS WORK IS SOLD WITH THE UNDERSTANDING THAT THE PUBLISHER IS NOT ENGAGED IN RENDERING LEGAL, ACCOUNTING, OR OTHER PROFESSIONAL SERVICES. IF PROFESSIONAL ASSISTANCE IS REQUIRED, THE SERVICES OF A COMPETENT PROFESSIONAL PERSON SHOULD BE SOUGHT. NEITHER THE PUBLISHER NOR THE AUTHOR SHALL BE LIABLE FOR DAMAGES ARISING HEREFROM. THE FACT THAT AN ORGANIZATION OR WEBSITE IS REFERRED TO IN THIS WORK AS A CITATION AND/OR A POTENTIAL SOURCE OF FURTHER INFORMATION DOES NOT MEAN THAT THE AUTHOR OR THE PUBLISHER ENDORSES THE INFORMATION THE ORGANIZATION OR WEBSITE MAY PROVIDE OR RECOMMENDATIONS IT MAY MAKE. FURTHER, READERS SHOULD BE AWARE THAT INTERNET WEBSITES LISTED IN THIS WORK MAY HAVE CHANGED OR DISAPPEARED BETWEEN WHEN THIS WORK WAS WRITTEN AND WHEN IT IS READ.

For general information on our other products and services please contact our Customer Care Department within the United States at (800) 762-2974, outside the United States at (317) 572-3993, or fax (317) 572-4002.

Trademarks: Wiley, the Wiley logo, Wrox, the Wrox logo, Programmer to Programmer, and related trade dress are trademarks or registered trademarks of John Wiley & Sons, Inc. and/or its affiliates, in the United States and other countries, and may not be used without written permission. All other trademarks are the property of their respective owners. Wiley Publishing, Inc., is not associated with any product or vendor mentioned in this book.

Wiley also publishes its books in a variety of electronic formats. Some content that appears in print may not be available in electronic books.

Dedication

I dedicate this book to my family, for enduring the time I’ve spent on the platform and the extra hours I’ve spent writing about it. They’ve all seen demos of features as they were being developed and as I’ve written about them. Heather, Josh, Kelley, and even little Adam know more than they probably want to know about web development.

-Matt Gibbs

I’d like to dedicate this book to my wife, Heedy, and my two boys, Danny and Jeffery.

-Dan Wahlin

About the Authors

Matt Gibbs is currently the development manager of the UI Framework and Services Team. This talented group is responsible for ASP.NET and the AJAX Framework as well as the new Integrated Pipeline of IIS 7. Matt has been working on Microsoft web technologies since joining the IIS 4 team to work on “classic” ASP in 1997. He has co-authored several books on ASP and ASP.NET.

Dan Wahlin (Microsoft Most Valuable Professional for ASP.NET and XML Web Services) is a .NET development instructor at Interface Technical Training (http://www.interfaceTraining.com). Dan founded the XML for ASP.NET Developers Website (www.XMLforASP.NET), which focuses on using XML, ADO.NET, and Web Services in Microsoft’s .NET platform. He’s also on the INETA Speakers Bureau and speaks at several conferences. Dan has co-authored/authored several different books on .NET, including ASP.NET 2.0 MVP Hacks and XML for ASP.NET Developers. When he’s not writing code, articles, or books, Dan enjoys writing and recording music and playing golf and basketball with his wife and kids. Dan regularly blogs at http://weblogs.asp.net/dwahlin and http://blogs.interfacett.com/dan-wahlins-blog.

Acknowledgments

First, I have to acknowledge the support of my family. They have tolerated my working on this book while we watch movies together, and they no longer think it’s unusual for me to work on my notebook through baseball and basketball practice. My kids have even volunteered to do editing work for me. But I suspect that wouldn’t come close to what Sara Shlaer has done to help shape my ramblings. Eric Engler and Spike Xavier have done wonders to transform, augment, and verify the text I provided. They both deserve medals. And thanks to Jim Minatel for holding it all together. And thanks to the ASP.NET team for their dedication and hard work. They are passionate about creating the best platform and tools possible for application development.

-Matt Gibbs

Any time I get involved with a book, my family tends to suffer some, as I have to lock myself in my office for a while to get all of the writing done (hopefully before the deadline). But through all of the books and articles, they’ve always been very understanding and patient, so I’d like to thank them for their support...and calling me when dinner is ready. I’d also like to thank my parents, Danny and Elaine, for raising me in an environment that promoted education and a lot of other fun activities such as sports. I couldn’t have asked for a better family. I love all of you!

Finally, I’d like to thank Jim Minatel for getting me involved with the book and Sara Shlaer for all of her hard work editing my chapters. She put in a lot of time to make sure that what I wrote made sense.

-Dan Wahlin


Credits

  • Senior Acquisitions Editor
    Jim Minatel

  • Development Editor
    Sara Shlaer

  • Technical Editors
    Eric Engler
    Spike Xavier

  • Production Editor
    William A. Barton

  • Copy Editor
    C. M. Jones

  • Editorial Manager
    Mary Beth Wakefield

  • Production Manager
    Tim Tate

  • Vice President and Executive Group Publisher
    Richard Swadley

  • Vice President and Executive Publisher
    Joseph B. Wikert

  • Graphics and Production Specialists
    Joyce Haughey
    Jennifer Mayberry
    Alicia B. South

  • Quality Control Technician
    Brian Walls

  • Project Coordinator
    Bill Ramsey

  • Proofreading
    Ian Golder, Word One

  • Indexing
    Johnna VanHoose Dinse

  • Anniversary Logo Design
    Richard Pacifico


Introduction

ASP.NET revolutionized web application development. The platform handles many of the complexities of creating web applications. And now ASP.NET AJAX takes the development platform even further. The line between rich client applications and traditionally less interactive browser-based applications is being further blurred. The Microsoft AJAX Library brings object-oriented programming to JavaScript development for modern browsers, and the ASP.NET 2.0 AJAX Extensions 1.0 make it easy to write rich web applications that communicate with the web server asynchronously. Again, the complexities are made easy by using ASP.NET. The new server controls that are part of ASP.NET AJAX make it simple to designate parts of the page to be updated automatically without making the user pause and wait while the data is refreshed. You can have partial page updates without writing a single line of code. Other new controls let you alert the user that background work is happening and designate regular intervals for updates to occur. And the ASP.NET AJAX Control Toolkit makes it easy to make your user interface really come to life with animations, modal dialogs, transition effects, and more.

Who This Book Is For

This book is aimed at experienced ASP.NET developers looking to add AJAX to their applications, and experienced web developers looking to move to ASP.NET and AJAX together.

In this book, I assume that you already have an understanding of how ASP.NET works. For an in-depth discussion of ASP.NET, I recommend Professional ASP.NET 2.0, by Bill Evjen, et al. (Wrox, 2005). The focus here is on how you can extend ASP.NET applications to update portions of the page asynchronously and to add richer UI elements to a page. ASP.NET AJAX makes it easy to enrich your existing application or to design a new application to provide a better experience for users. The differences among modern browsers have been abstracted, allowing you to write to a common set of APIs and trust that the user will get the correct behavior whether they are using Internet Explorer, Firefox, or Safari.

If you know how to author ASP.NET pages, you can easily start using the Microsoft AJAX library to manipulate the browser’s Document Object Model and communicate with the server to update the user’s view of data without forcing them to wait for the entire page to be refreshed.


What This Book Covers

This book covers ASP.NET 2.0 AJAX Extensions 1.0. It does not cover ASP.NET 2.0, on which ASP.NET AJAX is built. The examples lead you from the core of what is included in the ASP.NET AJAX Library through the core controls you would first start using. You build on that using the core JavaScript library and the ASP.NET AJAX Toolkit before covering debugging, deployment, and custom control development.

The ASP.NET 2.0 AJAX Extensions 1.0 release includes the Microsoft AJAX Library as well as the server controls that can be used in ASP.NET pages to extend applications, making them more rich and interactive.

It does so by leveraging the Microsoft AJAX Library, which is JavaScript that runs in the browser. The server controls and JavaScript library work together to let you update HTML with data obtained asynchronously from the server. The ASP.NET application services are exposed to JavaScript classes in the Microsoft AJAX Library, making authentication and personalization accessible from the browser.

Chapter 1 introduces you to ASP.NET AJAX. I discuss the need for AJAX libraries and explain how ASP.NET AJAX compares to other AJAX libraries. You’ll see how ASP.NET AJAX is composed of client and server pieces and that you can use the client library with any server platform you choose. In Chapter 2, I focus on the most popular and easily applied feature of ASP.NET, the UpdatePanel control. This control allows you to automatically update portions of a page asynchronously, without subjecting the user to a visible pause while the page refreshes. Chapters 3 and 4 give you some key information about working with JavaScript and how the Microsoft AJAX Library makes development with JavaScript easier. The book then works through several key features, including control of script resources and working with the ScriptManager control in Chapter 5, the networking objects in Chapter 6, and the application services in Chapter 7 that allow you to authenticate users as well as store and retrieve per-user information from the client stored on the server. Chapter 8 looks at the ASP.NET AJAX Toolkit and all it has to offer for creating rich user interfaces. Chapter 9 looks at what is required to test and debug AJAX applications, Chapter 10 explores how to deploy ASP.NET AJAX applications, and Chapter 11 shows you how to develop custom AJAX controls.


What You Need to Use This Book

ASP.NET 2.0 AJAX Extensions 1.0 builds on top of ASP.NET 2.0, so you will need to have version 2.0 of the .NET Framework installed. There are two separate types of download for the .NET Framework. The redistributable package is what is required for application to run and needs to be on development, test, and server machines. The SDK (Software Development Kit) is a set of tools for use in developing applications. Both are available from http://msdn2.microsoft.com/en-us/netframework/aa731542 .aspx. The examples use Visual Web Developer. The free Express version is available at http://msdn .microsoft.com/vstudio/express/web.


Conventions

To help you get the most from the text and keep track of what’s happening, we’ve used a number of conventions throughout the book.

Important

Boxes like this one hold important, not-to-be-forgotten information that is directly relevant to the surrounding text.

Tip

Tips, hints, tricks, and asides to the current discussion are offset and placed in italics like this.

As for styles in the text:

  • We highlight new terms and important words when we introduce them.

  • We show keyboard strokes like this: Ctrl+A.

  • We show file names, URLs, and code within the text like so: persistence.properties.

  • We present code in two different ways:

In code examples, we highlight new and important code with a gray background.

The gray highlighting is not used for code that's less important in the present
context or has been shown before.


Source Code

As you work through the examples in this book, you may choose either to type in all the code manually or to use the source code files that accompany the book. All of the source code used in this book is available for download at http://www.wrox.com. Once at the site, simply locate the book’s title (either by using the Search box or by using one of the title lists) and click the Download Code link on the book’s detail page to obtain all the source code for the book.

Tip

Because many books have similar titles, you may find it easiest to search by ISBN; this book’s ISBN is 978-0-470-10962-5.

Once you download the code, just decompress it with your favorite compression tool. Alternately, you can go to the main Wrox code download page at http://www.wrox.com/dynamic/books/ download.aspx to see the code available for this book and all other Wrox books.


Errata

We make every effort to ensure that there are no errors in the text or in the code. However, no one is perfect, and mistakes do occur. If you find an error in one of our books, such as a spelling mistake or faulty piece of code, we would be very grateful for your feedback. By sending in errata, you may save another reader hours of frustration. At the same time, you will be helping us provide even higher-quality information.

To find the errata page for this book, go to http://www.wrox.com and locate the title using the Search box or one of the title lists. Then, on the book details page, click the Book Errata link. On this page you can view all errata that has been submitted for this book and posted by Wrox editors. A complete book list including links to each book’s errata is also available at www.wrox.com/ misc-pages/booklist.shtml.

If you don’t spot “your” error on the Book Errata page, go to www.wrox.com/contact/ techsupport.shtml and complete the form there to send us the error you have found. We’ll check the information and, if appropriate, post a message to the book’s errata page and fix the problem in subsequent editions of the book.


p2p.wrox.com

For author and peer discussion, join the P2P forums at p2p.wrox.com. The forums are a web-based system for you to post messages relating to Wrox books and related technologies and interact with other readers and technology users. The forums offer a subscription feature to e-mail you topics of interest of your choosing when new posts are made to the forums. Wrox authors, editors, other industry experts, and your fellow readers are present on these forums.

At http://p2p.wrox.com you will find a number of different forums that will help you not only as you read this book, but also as you develop your own applications. To join the forums, just follow these steps:

  1. Go to p2p.wrox.com and click the Register link.

  2. Read the terms of use and click Agree.

  3. Complete the required information to join as well as any optional information you wish to provide and click Submit.

  4. You will receive an e-mail with information describing how to verify your account and complete the joining process.

Tip

You can read messages in the forums without joining P2P, but in order to post your own messages, you must join.

Once you join, you can post new messages and respond to messages other users post. You can read messages at any time on the web. If you would like to have new messages from a particular forum e-mailed to you, click the Subscribe to this Forum icon by the forum name in the forum listing.

For more information about how to use the Wrox P2P, be sure to read the P2P FAQs for answers to questions about how the forum software works as well as many common questions specific to P2P and Wrox books. To read the FAQs, click the FAQ link on any P2P page.


Chapter 1: Developing Next-Generation Web Applications

Overview

Web applications have historically been less rich and responsive than desktop applications. End users don’t necessarily understand the details of how an application works, but they know that interacting with a website in the browser is distinctly different from using an application installed locally. When a development team tackles a new project, one of the first questions they are faced with is whether end users can accept the limitations of web development or whether they need to require a client desktop application to be installed. Web applications are accessible from just about any browser, just about anywhere, but they are limited by what you can do with markup and script code running in the browser.

Desktop applications, also called fat client applications, require that the user perform an installation on their machine, but let developers leverage the advanced mouse and graphics capabilities of the operating system that would be extremely difficult to implement in a web browser, and also take advantage of the user’s machine for tasks such as offline storage. Conversely, web applications can be updated just by changing what is running on the server, and site visitors get the latest version instantaneously. However, it’s much more difficult to update a desktop application, because you’d have to get users to perform yet another installation or else ensure that the application has been coded to include a clever system for doing updates automatically.

Web applications are said to use a zero-deployment model, but desktop applications use a heavy deployment and configuration model. The choice is often characterized as a tradeoff between rich and reach: Desktop applications generally offer a richer user experience than what could be offered in the browser, but with a web application you are able to reach users anywhere on any OS with almost no extra effort. Further, many companies have restrictive policies in place regarding what software can be installed on employees’ machines, and they often don’t allow employees to have administrative access that is required to install new applications, so web applications will be the only viable option in many situations.


Bringing Richness to Web Applications

Years ago, having a web presence was a distinguishing factor for companies. That is no longer the case. Now just having a web presence is no longer enough. Companies are distinguishing themselves further through web applications that react intuitively to customer actions and anticipate user input. This book shows you how ASP.NET AJAX addresses specific web development challenges and paves the way for taking your website to another level of user experience. In this chapter, I discuss the need for richer frameworks in web application development. I talk about the key pieces of the ASP.NET AJAX platform and highlight some other options.

The fundamental set of technologies that enable the next generation of web applications are not new. Online news articles and blogs point to Google, Flickr, and several other services as prime examples of leveraging these technologies in unique ways. The applications have some unique features, but in reality, the underlying technologies have been around and in use for nearly a decade. Take a look at how Microsoft Exchange Server provided rich access to email from a web browser in the Outlook Web Access application, and you can see that the concept of ubiquitous access from a browser while leveraging a common set of browser features for a rich user experience has been around and in practice for years. Users get a remarkably full-featured application with no local installation and are able to access e-mail from virtually any machine.

The technologies in use for building rich applications are generally referred to as AJAX (Asynchronous JavaScript and XML). While the acronym is nice, it doesn’t do much to explain what is actually happening. Instead of building a web application to be just a series of page views and post backs, developers are using JavaScript to communicate asynchronously with the web server and update parts of the page dynamically. This means that the web page can dynamically adapt its appearance as the user interacts with it, and it can even post or fetch data to or from the web server in the background. Gone are the days of the ugly post back, which clears the user’s screen and breaks his concentration! Instead, we need to post back now only if we want to change to a different web page.

Tip

Even that rule can be bent. Some applications are pushing this boundary and completely changing the user’s view, just as though they navigated to a new page, but they do so through an asynchronous post and by changing the page content without actually navigating to a new URL.

The AJAX acronym refers to XML as the data format being exchanged between client and server, but in reality, applications are being built that retrieve simple pieces of text, XML, and JSON (JavaScript Object Notation) (which I discuss in more detail in Chapter 4). Part of the AJAX appeal isn’t even covered by the acronym: In addition to communicating with the server without blocking, developers are leveraging Dynamic HTML (DHTML) and Cascading Style Sheets (CSS) to create truly amazing user interfaces. JavaScript code running on the client communicates asynchronously with the server and then uses DHTML to dynamically modify the page, which supports rich animations, transitions, and updates to the content while the user continues interacting with the page. In many cases, users won’t even realize they are using a web application!


Who Benefits from AJAX?

AJAX offers benefits to both end users and developers. For end users, it reduces the “rich or reach” conflict; for developers, it helps in overcoming the constraints raised by HTTP.

Why End Users Want AJAX Applications

Users tend to view desktop applications as a sort of commitment. They install a program, usually from a disk pulled from a costly shrink-wrapped box. The program consumes hard disk space as well as a position in the program menu. The user may need to update the program periodically or perform an upgrade later on to get new features. If the program is proactive about updating itself, the user is confronted regularly with dialogs about accepting patches or downloads. In exchange for this investment of time, money, and energy, the user gets repaid by an application that is able to leverage the operating system and machine resources. It is a rich application. It has local storage capabilities, offers quick response times, and can present a compelling and intuitive graphical user interface.

More and more applications are accessible from the web browser, where the full resources of the hardware and OS are not available, but the user commitment of a desktop application is not required. Over the years, interacting with a web application has meant a predictable pattern for users. They click a link in the page and the browser flashes while the user waits until the screen is repainted (the dreaded post back). This cycle is repeated again and again. The user looks at what is presented on the page, interacts with it, and clicks somewhere. The browser produces an audible click for feedback and begins to post back to the server. The screen of the web browser flashes blank and some icon spins or flashes while the user waits for a new version of the page to be returned from the server. Many times, the new version of the page is almost exactly the same as the previous version, with only part of the page being updated. And then the cycle begins all over again. This has a sluggish feeling even when the user has a high-speed network connection.

The AJAX set of technologies has changed what users expect from web applications. JavaScript code running in the browser works to exchange data with the web server asynchronously. There is no click sound and the browser does not flash. The request to the server is nonblocking, which means the user is able to continue viewing the page and interacting with it. The script gets the updated data from the server and modifies the page dynamically using the DHTML coding methodology. The user is able to continue looking at the page while parts of it are updated in the background. AJAX is used to provide a more responsive experience, making web applications behave more like desktop installations. JavaScript is used to provide a richer experience with support for drag-and-drop, modal dialogs, and seemingly instantaneous updates to various parts of the page based on user inputs.

A big part of successfully leveraging AJAX technologies is in the perceived performance increase. Users appreciate web applications that anticipate their actions. If you also use JavaScript code in the background to pre-fetch images and data that may be needed, users can get a speedy response without the usual pause that accompanies their actions. Nobody wants to wait for data exchanges between client and server; studies have shown that a time lag between user input and subsequent UI changes can significantly reduce their productivity and give them the frustrating feeling that they are fighting the application. Users want web applications to behave like desktop installations but without the overhead associated with an installation. As more applications employ smart caching, anticipate user actions, and provide richer UI, the difference between web and desktop applications is becoming blurred. Expectations of web applications are rising. The end user has now seen that it is possible to avoid the commitment of installing a desktop application and still have a rich and responsive experience.

Why Developers Want AJAX

Often, the first question to arise when starting a new development project is what type of application it will be. Should it be a desktop application or a web application? This is a key decision because it has historically dictated a lot about the nature of the application and the development problem space. Many developers are now choosing to build web applications by default unless something about the application dictates that it must be a desktop install. If it must run offline or if it requires a user interface that is complex to achieve in HTML, targeting the web browser may be ruled out, and the choice to write a standalone application is forced.

Developers have a difficult job writing modern web applications due to the inherent worldwide-web functionality constraints imposed by the use of the Hypertext Transfer Protocol (HTTP) and the way browsers use it. HTTP is a stateless protocol. The web browser requests a page, possibly carrying some querystring or form input parameters, and the server processes the request and sends a response that includes HTML-rendered content. The server can only react to the information supplied in the current request and doesn’t know, based on the information in the request itself, details about the path the user took to get to the current view. When the response is rendered, the connection may be broken and the server won’t have any information to preserve for the next request. From the server’s perspective, it is simply listening for requests to come in from any browser anywhere and then reacting. The browser issues a request to the page and receives an HTML page in response. It uses the HTML it receives to render the user interface. The user interacts with the page, and, in response, the browser clears the screen and submits a new request to the server, carrying some information about user input or actions. Again, a complete HTML page is returned. The browser then presents the new version of HTML. Fundamentally, the HTTP protocol is stateless. The server gets a request and responds to it. The request carries limited information about the ongoing conversation that is happening between client and server.

AJAX makes this much better. AJAX breaks this pattern by updating portions of the page separately, via partial page rendering. Figure 1-1 shows a typical non-AJAX series of browser and server interactions. Each request results in a full page rendering, and, in response, the browser updates the user’s entire view.

Image from book
Figure 1-1

In Figure 1-2, AJAX is employed to improve the user’s experience. A request is made for the initial page rendering. After that, asynchronous requests to the server are made. An asynchronous request is a background request to send or receive data in an entirely nonvisual manner. They are asynchronous because the user interface isn’t frozen during this time, and users can continue interacting with the page while the data transfer is taking place. These calls get just an incremental update for the page instead of getting an entirely new page. JavaScript running on the client reacts to the new data and updates various portions of the page, as desired. The number of requests to the server may be no different, or in some cases there may actually be more calls to the server, but the user perception is that the application feels more responsive. They aren’t forced to pause, even if it’s only a slight pause, and wait for the server while staring at a blank browser screen.

Image from book
Figure 1-2

Almost a decade ago, the Microsoft Exchange Server team created an ActiveX control called XmlHttpRequest that could be instantiated from JavaScript and used to communicate with the server. This can occur without clearing the screen to paint a whole new page. Using the XmlHttpRequest object, you could send information to the server and get data back without requiring a whole new HTML page. JavaScript code could then manipulate the HTML dynamically on the client, avoiding the annoying flash and the wait that users associate with web browsing. This functionality was not limited to Internet Explorer for long. Soon, other browsers included XmlHttpRequest objects as well. Developers could now write richer applications with reach extending across various operating systems.

The browsers also created an advanced DOM (Document Object Model) to represent the browser, the window, the page, and the HTML elements it contained. The DOM exposed events and responded to input, allowing the page to be manipulated with script. Dynamic HTML (DHTML) opened the door to writing rich interfaces hosted within the web browser. Developers started writing hundreds and even thousands of lines of JavaScript code to make rich and compelling applications that would not require any client installation and could be accessed from any browser anywhere. Web applications began to move to a whole new level of richness. Without AJAX libraries, you would be faced with writing lots and lots of JavaScript code and debugging the sometimes subtle variations in different browsers to reach this new level of richness.

Image from book
JavaScript Libraries and AJAX

Developers have had access to AJAX technologies for years, and many have been leveraging AJAX to push the limits of what can be done in the web browser. But what is really making AJAX more compelling now are the comprehensive script libraries and integration with server technologies that make it easier to write rich web applications and avoid the need to become an expert on the different versions of JavaScript. A JavaScript library is referenced within the HTML of a page using the <script> tag:

<html>
  <head>
    <script src="http://theGibbs.us/someScript.js"
type="text/javascript"></script>
  </head>
...

The script is downloaded and cached by the browser. Other pages within the application can reference the same URL for script, and the browser won’t even bother to go back to the server to get it. The functionality of that script file is available for use from within the page rendered to the browser. A script library sent to the browser and then leveraged for writing a richer UI and a more responsive application is at the heart of all AJAX libraries.

Image from book


What Is ASP.NET AJAX?

ASP.NET AJAX is the name of Microsoft’s AJAX solution, and it refers to a set of client and server technologies that focus on improving web development with Visual Studio. Other companies have their own AJAX solution, often taking a radically different approach, but Microsoft has sought to build upon the popular ASP.NET technology by developing a comprehensive set of script libraries as well as server programming enhancements. One piece, the Microsoft AJAX Library, is a JavaScript library that works on a variety of browsers and serves to simplify JavaScript development. It provides you with the capability to easily write JavaScript using an object-oriented programming approach similar to code-behind programming in ASP.NET. You can use the functions of the Microsoft AJAX library to interact with the DOM, dynamically update portions of the page, communicate with the web server asynchronously, and write rich animations on the page. You’ll learn about the client features in Chapters 6 and 7, but for now think of the Microsoft AJAX Library as a type system and set of class libraries that simplify writing JavaScript to enhance the user experience, manipulate the DOM, and communicate with the web server. The huge benefit in using this library is that it abstracts the tedious low-level DHTML programming into an OOP model that is much easier to work with.

The other part of the ASP.NET AJAX release is the server-side ASP.NET 2.0 AJAX Extensions. These extensions build on top of the ASP.NET classes and controls and leverage the Microsoft AJAX Library sent to the browser. They make it easy to quickly take advantage of AJAX technologies to enrich an application. Through a set of standard web services, ASP.NET AJAX is also able to leverage server-based application services such as authentication and user profile storage. The extensions both extend some of the existing ASP.NET controls by adding new behaviors and include a new set of server controls that make it easy to do asynchronous updates to a page and leverage server resources.

The client and server pieces of ASP.NET are shown in Figure 1-3. ASP.NET is built on top of the Microsoft Internet Information Services (IIS) web server. ASP.NET AJAX builds on top of that and the web services it includes. The Microsoft AJAX Library runs in the browser to manipulate the DOM, communicate asynchronously with the web server, and take advantage of ASP.NET services.

Image from book
Figure 1-3
Image from book
Installing ASP.NET AJAX

The ASP.NET AJAX Extensions are an add-on to ASP.NET 2.0. There is a link to the installer available at http://ajax.asp.net/downloads/default.aspx?tabid=47. When you run the installer, it will install the System.Web.Extensions.dll assembly into the Global Assembly Cache (gac) of your machine. Being in the gac allows the assembly to assert the permissions necessary to provide the full range of functionality ASP.NET AJAX offers. If Visual Studio 2005 or Visual Web Developer is present on the machine, the installer will also set up ASP.NET AJAX project templates. This allows you to create a new ASP.NET AJAX–enabled website. The web.config file and compilation references will have everything necessary to start building AJAX applications. The toolbox will include the UpdatePanel control discussed in Chapter 2, and the ScriptManager control covered in Chapter 5. The details of what is included in the new web.config file are discussed in detail in Chapter 10.

Image from book

The ASP.NET Component

The original (ASP) Active Server Pages technology was released as part of Internet Information Server 3.0. It was then improved with support for transactions and access to COM objects with the release of the NT 4.0 Option Pack almost ten years ago. At the time, most websites consisted of static HTML pages. To the extent that there were any dynamic applications, they were CGI (Common Gateway Interface) or ISAPI (Internet Server API) applications typically written in C and C++. With the release of Active Server Pages (ASP), developers could use JavaScript or VBScript on the server to leverage a set of intrinsic objects provided by ASP. “Classic ASP,” as it is now called, provided a session object so that developers didn’t need to worry about the stateless nature of HTTP. It had Request and Response objects that provided for easy access to data coming from forms on the client and a way to send updated information back. The Server and Application objects were an avenue for accessing information from the web server and utilizing a common set of COM objects across the application.

Although Classic ASP was a big win for developers, developers soon found that developing complex applications became difficult. The lack of support for modularization meant that applications ended up with such complex interdependencies in script that developers often referred to it as “spaghetti code.” Debugging support was severely lacking, and there were no rich design tools for developing applications. Performance also suffered because ASP used a slow scripting model, and syntax errors could only be detected at runtime. ASP.NET is a big leap forward. It provides compatibility for existing applications by including the same set of intrinsic objects found in Classic ASP but moves from interpreting JavaScript and VBScript on the fly to using a compiled set of pages and modules written in C# and VB.NET. Classic ASP simply executed script in order from the top to the bottom of the page; ASP.NET is an event-driven model with a page lifecycle, making it more like writing a desktop application. Now, instead of including separate JavaScript files to represent business objects, you can create objects in any .NET language and access them directly from ASP.NET.

ASP.NET takes a set of pages that contain code and markup and generates a Page class that is then compiled and cached. For each request to the page, the class is instantiated and a complete page lifecycle is executed. A set of events are executed, some of which have been overridden by the generated Page class. Controls in the page also participate in the lifecycle, databinding to backend databases, reacting to user input, and dealing with changes to their state from the user’s previous view. To react to user actions, the developer has only to provide an event handler for the given action. For example, the button control exposes a Click event. When using it, you don’t need to write code to examine all form variables on a page to know if the button was clicked. Instead, you just provide code for an event handler override. The event handler code can then update the HTML for the page or the properties and data of other controls on the page.

Tip

This book assumes some familiarity with ASP.NET as you look at how the new features of ASP.NET AJAX extend its functionality and provide ways to enhance web applications. If you are new to ASP.NET, I recommend looking at the set of QuickStart tutorials available at http://www.asp.net/ quickstart. Other excellent resources are Beginning ASP.NET 2.0 (VB.NET), Beginning ASP.NET 2.0 with C#, and Professional ASP.NET 2.0, all from Wrox Press. The ASP.NET website (http:// forums.asp.net/) also has an extensive set of discussion forums where you can interact with fellow ASP.NET developers as well as members of the ASP.NET team.

The JavaScript Component

AJAX technologies take advantage of the common support for JavaScript found in modern browsers. Because there is a standard that is supported across the various browsers, you can write scripts knowing that they will run. This wasn’t always the case.

In the mid 1990’s, Netscape and Microsoft (along with others) collaborated on a standard for a scripting language that they would support in their web browsers. The standard is called EcmaScript. Microsoft’s implementation is called JScript, but the language is generally referred to as JavaScript, as it was called in Netscape. (It has nothing to do with Java, but someone must have thought the association was useful for marketing purposes.) JavaScript program snippets are sent down to the browser along with the HTML, and they run inside the user’s browser to affect how the page is processed on the client.

JavaScript is not compiled; it is interpreted. There is no static type-checking like you get in C++ and C#. You can declare a variable without needing to specify a type, and the type to which the variable refers can change at any time. This makes it easy to get started programming in JavaScript, but there’s inevitably a certain amount of danger in allowing the data type of a variable to change dynamically at runtime. In the following snippet, notice that the variable can reference any type without difficulty:

var something = 1;
something = true;
something = "a string";

JavaScript is a dynamic language. Types can actually be extended during program execution by other code. This means that you can write code that creates types on the fly. Because there is no enforcement of type safety, your code can receive these types as parameters or return values without any problem. This provides a great degree of flexibility and coding power.

The fundamental types in JavaScript are strings, numbers, Booleans, and functions. There is also support for objects and arrays, which are collections of the fundamental types. Some additional objects are included that are considered essential for many programming tasks. This includes support for regular expressions and date and time operations.

You can use the plus operator on strings in JavaScript to concatenate them:

var theEnd = "THE END.";
var result = "Beginning, " + "middle, and " + theEnd;

In this example, the result variable is now the string: “Beginning, middle, and THE END.”

JavaScript interpreters use the IEEE floating-point standard for storing numbers. Ignoring the gory details, you can assume that for most programming tasks you won’t have any trouble.

The Boolean type in JavaScript is about what you would expect it to be but maybe not exactly so. The Boolean represents whether or not an expression is true, but it uses the C-style convention using integer values 0 and 1.

Variables can exist in JavaScript without having a value, and a variable may simply be undefined, which can produce unexpected results. In this piece of JavaScript, three variables are declared, and all of these comparisons are designed to return a true value.

<script type="text/javascript">
var one = 1;
var zero = 0;
var undefinedVar;

if(one) {
    alert("1 is true");
}

if(!zero) {
    alert("0 is false");
}

if(!undefinedVar) {
    // this test tells us that "undefinedVar" either contains 0,
    // or is really undefined: both of which equate to false
    alert("undefinedVar is false");
}

if(one != zero) {
    alert("one and zero are not the same");
}
</script>

You can check specifically to see if a variable has been defined like this:

if( typeof(undefinedVar ) == "undefined" ) {
  alert("undefinedVar is undefined");
}

Variables can also have a null value, which is not the same thing as being undefined, as a null value does constitute a value.

Functions are also real types in JavaScript. They can accept arguments and return values. Functions can be passed to other functions and can be created dynamically by other script code.

Here are two equivalent definitions for a function called Add that will take two variables and return the result of applying the plus operator. Notice that I didn’t state that it takes two numbers. Remember, JavaScript variables don’t have a defined type, so I could just as easily pass two strings and get them concatenated by my Add function.

<script type="text/javascript">
function Add(x, y) {
    return x + y;
}
var AddAgain = function(x, y) { return x + y; }
</script>

Once either of these styles is used to create a function, it can be called from that scope and any nested scope to perform the addition. There is no advantage to one of these forms over the other. You can simply choose to use the syntax that you prefer.

<script type="text/javascript">
var result = Add(36, 24);
alert(result);  //displays 60

var stringResult = Add("Hello ", "there.");
alert(stringResult); //displays "Hello there."
</script>

Objects and arrays are just collections of other types. Array types do not require that the values they hold be named; instead, you can access them by index. The values held in an object are referenced by field or property names. Objects can also hold functions (which can be accessor functions to give public visibility to local variables), which lets you create data structures that represent entities in JavaScript code. Missing from this sort of object-oriented programming is a concept of type inheritance. The Microsoft AJAX Library provides a set of classes and recommended patterns for achieving inheritance in JavaScript, making it more natural for switching between JavaScript and other high-level languages. The following code example includes a definition for an Album object that holds and returns the artist and album title. An array is then used to store information about several albums.

<script type="text/javascript">
// define an object named Album – note that this object is typeless
Album = function(title, artist) {
    var _title = title;
    var _artist = artist;

    this.get_title = function() { return _title; }
    this.get_artist = function() { return _artist; }
}

// create object instances by calling the constructor
var albumA = new Album("Rift", "Phish");
var albumB = new Album("A Picture of Nectar", "Phish");

// create an array to hold the instances (also typeless)
var albumArray = new Array();

albumArray[0] = albumA;
albumArray[1] = albumB;

// iterate over the array to show the album titles
for(var i = 0; i < albumArray.length; i++) {
  alert((albumArray[i]).get_title()); // call get_title accessor
}
</script>

The Web Services Component

The fundamental concept of web services is powerful and continues to evolve and advance. The original SOAP (Simple Object Access Protocol) standard is the use of the HTTP protocol to pass XML-formatted data to the server from a client and receive XML-formatted results in return. This can be from within a web browser using the XmlHttpRequest object or directly from a desktop application or another server. Before web services became widely adopted, it was not uncommon for developers to programmatically request a page as an HTML document and extract the desired data from it, a technique known as screen-scraping. This causes all sorts of frustrations as sites are continually updated and the screen-scraping clients must try to keep up by modifying their parsing code to adapt to the new HTML the target site is rendering.

This produced frustration, as sites that presented data using HTML visual pages were prone to modifying those pages and this would break the screen-scraping program, which expected to see the data in the original format. Web services were created as a nonvisual way to transfer data over the web, and they are the natural way to isolate remote method calls from the presentation layer. Now, instead of screen-scraping, you are able to call a web service and get XML-formatted data returned that is easily consumed by a program.

By passing plain text data formatted as XML and by eliminating the visual elements, data passed in web services is much easier to parse than HTML. And, since XML can contain an embedded schema, code can inspect the schema and use it to determine the structure and types used in the data. You can extend the schema passed with the data being returned without worrying that consuming applications will be broken, and therefore XML readers can be somewhat tolerant of modifications that would have certainly caused a screen-scraper a great deal of grief!

The schema for data can be extended without requiring all consumers to be updated. Consumers can easily get the parts of the XML document they wish to process and disregard the rest. This has progressed beyond simple XML formats. Unlike previous implementations of web services, you can now define web service contracts to be built to employ arbitrary encoding and utilize any one of a number of wire protocols. What drives the web service concept is the ability to access data easily from various applications in a loosely coupled way, and the new Microsoft Windows Communication Foundation (WCF) takes this concept to a whole new level, allowing the contract to specify wire protocols, deployment strategies, and logging infrastructure, along with providing support for transactions.

ASP.NET AJAX provides a set of JavaScript proxy objects to access some new web services built-in to ASP.NET. Profile information, membership services, and role management can be easily accessed from the client. Developers don’t need to create their own infrastructure to support these fundamental application services, but can include a few lines of code to take advantage of server resources from JavaScript code running in the browser, thereby dramatically extending the reach of ASP.NET to include both the client and the server. And because the JavaScript libraries are designed to be easy to use for developers already familiar with server-side .NET programming, all of this extra functionality comes in a friendly package that is easy to leverage.

The Dynamic HTML Component

Dynamic HTML is not a freestanding technology. It is the use of a set of technologies in a specific way. HTML is returned to the browser following a web server request. The browser then renders the page, and the user is able to view it. The browser also exposes the Document Object Model (DOM) that represents the structure of the HTML being displayed. The DOM can be accessed from JavaScript embedded in, or referenced by, the page. The appearance of the HTML is affected by applying CSS (Cascading Style Sheets) that control colors, fonts, position, visibility, and more. You can bind JavaScript code to events that the browser will raise when users perform certain actions, such as hovering over a particular ele-ment or entering text in a textbox. The JavaScript code can update text or manipulate the CSS settings for elements within the page. And, of course, it can communicate with the server to expand the dynamic nature of the page even further. The user will see a dynamically changing user interface that responds to his actions in real-time, which will greatly enhance his overall experience, thereby increasing his productivity and satisfaction with the application.


Other AJAX Libraries

In addition to ASP.NET AJAX, many third-party AJAX libraries are available that can be used with ASP.NET, although not all of them were specifically designed for it. Some are mostly focused on providing JavaScript libraries for use from within the browser to make manipulation of the browser DOM (Document Object Model) easier. Others include some level of server functionality for use within ASP.NET pages (where server controls will render on the client side). This section briefly highlights some of what these libraries offer. The ASP.NET AJAX Framework can coexist with script and controls from other libraries, although given the dynamic nature of the JavaScript language, it is possible to extend types so that they conflict with each other. Mixing and matching libraries might work just fine for many uses, but you might find conflicts in other cases.

  • Ajax.NET Professional: Michael Schwartz developed Ajax.NET Professional as a tool primarily used to simplify the data transport mechanism that enables a client JavaScript routine to communicate with a server program. The server code is simple to use: You merely need to register the control in your page and decorate some code-behind methods with attributes to designate which ones can be called from the client. Then you can leverage the script library to make the calls and pass data. This is intended for developers who are well versed with DHTML, and there aren’t many prebuilt visual controls. This is a lightweight solution with very little overhead in terms of bytes transferred and processing cycles needed on the client and server. The source code is available, and the package is free (http://www.ajaxpro.info).

  • Anthem.NET: Anthem.NET is a SourceForge project where users are able to download the sources to the project. It targets ASP.NET 1.1 and ASP.NET 2.0. It has a set of server controls that use their underlying JavaScript library to communicate with the server. They provide the ability to access the state of controls on the page during an asynchronous callback. At the time of writing, the Anthem.NET web page (http://anthem-dot-net.sourceforge.net) points out that the Anthem.NET user needs to be an experienced ASP.NET developer to get the most out of it. However, this is generally easier to use than Ajax.NET Professional, especially for developers who aren’t well-versed in DHTML. This project is similar to ASP.NET Ajax in many ways but isn’t as comprehensive.

  • DoJo: The DoJo toolkit can be found at http://dojotoolkit.com. It is a client-side library for AJAX development without ties to any server technology. DoJo has a type system for JavaScript and a function for binding script to events from JavaScript objects or DHTML elements. One of its strengths is rich support for dynamic script loading. You can specify dependencies and ordering in the way that scripts are retrieved and processed.

  • Prototype: The Prototype script library is available at http://prototype.conio.net. It does not target any server technology for integration. It has a type system for scripting in a more object-oriented way, along with some shortcut syntaxes for dealing with JavaScript arrays as well as accessing and manipulating HTML elements on the page. Prototype provides networking functionality and a method for automatically updating an HTML element with the results of an HTTP request when given a URL. The Prototype library also has functions for associating script objects and methods with DOM objects and events. The library is focused on simplifying tasks that can be cumbersome and tedious. It doesn’t provide much help for producing a richer user interface but puts forth the building blocks for an improved web-scripting experience.

  • Script.aculo.us: The Script.aculo.us library can be found at the website of the same name: http://script.aculo.us. Their tagline is “it’s about the user interface, baby!” which accurately describes their focus. Script.aculo.us is built on top of the Prototype library and picks up where it stops. It includes functionality for adding drag-and-drop support to an application. It has a lot of effects code for fading, shrinking, moving, and otherwise animating DOM elements. Script.aculo.us also has a slider control and a library for manipulating lists of elements.

  • Rico: The Rico library also builds on top of the Prototype system. It has support for adding drag-and-drop behavior to browser DOM elements. It also has some controls to bind JavaScript objects to DOM elements for manipulating data. Rico has constructs for revealing and hiding portions of a page using an accordion style. It also has animation, sizing, and fading effects prebuilt for easier use. These UI helpers are available at http://openrico.org.


Balancing Client and Server Programming with ASP.NET AJAX

Without the advanced use of JavaScript running in the browser, web applications have their logic running on the server. This means a lot of page refreshes for potentially small updates to the user’s view. With AJAX, much of the logic surrounding user interactions can be moved to the client. This presents its own set of challenges. Some examples of AJAX use include streaming large datasets to the browser that are managed entirely in JavaScript. While JavaScript is a powerful language, the debugging facilities and options for error handling are very limited. Putting complex application logic on the client can take a lot of time, effort, and patience. ASP.NET AJAX allows you to naturally migrate some parts of the application processing to the client while leveraging partial page rendering to let the server control some aspects of the page view.

Some websites make an application run entirely from a single page request, where JavaScript and AJAX will do a great deal of work. This presents some tough challenges. Users generally expect that the Back button will take them to the state of the application they were just viewing, but with AJAX applications this is not necessarily the case. The client may be sending some information to the server for persistent state management (perhaps in server memory or a database), but this requires extra code and special attention to error handling and recovery.

The richest, most maintainable applications seem to be those that balance client and server resources to provide quick response times, easy access to server resources, and a minimum of blocking operations while new page views are fetched.

ASP.NET AJAX provides a mix of client and server programming features. The Microsoft AJAX Library is aimed at client development. It provides a type system for an object-oriented approach to JavaScript development. It makes it easy to register code to respond to events. It provides useful functions to simplify common tasks like finding elements on the page, attaching event handlers, and accessing the server. The server features include functionality for managing JavaScript code to be sent to the client, declaring regions of the page to be updated asynchronously, creating timers for continuous updates, and accessing ASP.NET services such as user profile data and authentication.


Summary

The web has evolved over the last decade from providing a static presence to being the default choice for developers writing applications. With web applications, you get reach without having to deal with deployment and servicing issues that accompany desktop applications. But the bar continues to move higher for web applications as users come to expect more. AJAX technologies are driving web applications to rival rich desktop apps. You can use the results of asynchronous communication with the web server to update portions of the page without forcing the user to stop his work and wait for the page to post back and repaint. Dynamic HTML allows you to create a rich GUI with transitions and animations leveraging CSS for colors, fonts, positioning, and more.

ASP.NET AJAX includes the Microsoft AJAX Library that makes writing browser-based JavaScript easier and simplifies many common client programming tasks. It is easy to attach code to DOM events, write JavaScript in an object-oriented way, and access the server for persistent storage, authentication, and updated data.

ASP.NET AJAX also includes extensions to version 2.0 of the .NET Framework that can greatly improve your web application. There is built-in support for returning data in the JSON format that is easily consumed by JavaScript in the browser.

In this book, you will see how the client and server features of ASP.NET AJAX make it easier to push the limits of what you can do with a web application! You will learn how to update portions of a page asynchronously and how to manage the scripts that are used in the browser. You’ll find out how to use the networking facilities, with a dive into accessing ASP.NET services like authentication and profile storage. You’ll get a closer look at the JavaScript language and how the Microsoft AJAX Library builds on top of the language to simplify programming tasks. You’ll also see what ASP.NET AJAX offers for adding richer UI to web applications and look at how to debug and test web applications. And you’ll learn about some details of deploying and debugging applications and look at other resources available for use with ASP.NET AJAX.


Chapter 2: Partial Page Updates

If I had to sum up what AJAX is all about in a simple phrase, it would be “an improved experience for the end user through partial page updates and a better user interface.” At the core of the ASP.NET AJAX offering is the UpdatePanel control, which adds the ability to have portions of a page be updated asynchronously. The other half of the equation is richer user interface (UI) elements: controls that leverage the browser features better or extenders that enhance the functionality of other controls.

In this chapter, you will learn how to use the UpdatePanel control and also learn about its current limitations. In addition to working with the UpdatePanel in .NET code that runs on the server, you will also learn how to work with its client-side counterpart, the PageRequestManager, using JavaScript code running in the browser to get more fine-grained control over the asynchronous updates.

I will also demonstrate the UpdateProgress control, which gives the user a visual indication that something is happening in the background so he won’t lose patience with a page that stops responding momentarily, and I’ll show you the Timer control, which makes it easy to schedule updates to occur at regular intervals. Finally, the chapter covers the accessibility concerns of partial page rendering. Many developers put in a lot of effort to ensure that their web application works well with screen readers, and partial page rendering can make that task more difficult if it is not approached carefully.

The UpdatePanel Control

Usually, when a postback occurs, the entire page is reloaded. There is a visible flicker of the browser as the previous rendering is removed and the new rendering is displayed. The user will likely notice the interruption, and this can interfere with his train of thought. This is different from the smooth responsiveness we are accustomed to with desktop applications and can be disconcerting or annoying to users. This click-pause-flicker pattern is what the UpdatePanel helps you overcome. A web application can now be almost as responsive as a fat client Windows application! The small delays required for passing data between the client and the server are still there, but they are much easier to tolerate now. In many cases, the user may not even notice these delays, because we can distribute the updates so they don’t all occur at once.

The UpdatePanel is a container control without any UI of its own. It allows you to mark page regions as eligible to be refreshed independently. When a control within the UpdatePanel triggers a postback, the UpdatePanel intervenes to initiate the postback asynchronously and update just that portion of the page. The term asynchronously means that we don’t have to stop and wait for the result from the server. Rather, we can continue servicing the page with other JavaScript code, and the user can interact with other controls while we’re waiting for the response from the server. At the time we make the call to the server, we provide the name of a JavaScript callback function that will be called when the response has been received. That callback function will receive the results and update various page controls accordingly.

Listing 2-1 (BasicUpdatePanel.aspx) demonstrates the most basic usage of the UpdatePanel. The page has two labels and two buttons that can each cause a postback. It simply updates the two labels with the current server time. Button1 and its label are placed inside an UpdatePanel. Actually, they are put in the ContentTemplate of the UpdatePanel. Controls and markup placed in an UpdatePanel must reside in the ContentTemplate. The other label and button are left outside the UpdatePanel. There is also a ScriptManager control on the page. One instance of the ScriptManager must be placed on every page that uses AJAX. The ScriptManager control will be covered in detail in Chapter 5.

Listing 2-1
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server" language="C#" >
    protected override void OnLoad(EventArgs e) {
        base.OnLoad(e);
        string theTime = DateTime.Now.ToLongTimeString();
        for (int i = 0; i < 3; i++) {
            theTime += "<br />" + theTime;
        }
        time1.Text = theTime;
        time2.Text = theTime;
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Basic Update Panel</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
    <asp:Label runat="server" ID="time1"></asp:Label><br /><br />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
   <ContentTemplate>
       <div style="border-style:solid;background-color:gray;">
         <asp:Label runat="server" ID="time2"></asp:Label><br />
         <asp:Button ID="Button1" runat="server" Text="Inside Button" />
         </div><br />
   </ContentTemplate>
</asp:UpdatePanel>
    <asp:Button ID="Button2" runat="server" Text="Outside Button" />
</div>
</form>
</body>
</html>
Image from book

In the code-behind, both labels are assigned the same time string in the same way. When the page is first requested, the two labels show the same time. This is shown in Figure 2-1.

Image from book
Figure 2-1

When the Outside Button is clicked, a postback occurs and the time is updated for both of the labels. But when the button inside the UpdatePanel is clicked, an asynchronous postback occurs and only the label inside the UpdatePanel is updated. There is no flash or flicker as the content is changed with new data from the server. The result of clicking the Inside Button is shown in Figure 2-2. The time in the UpdatePanel has been updated, but the other time display has not.

Image from book
Figure 2-2

This highlights a key point about the UpdatePanel: When using the UpdatePanel, the page executes just as though it were a regular postback. You saw that there was no code on the page that did anything specific for partial page updates. The code never made reference to the UpdatePanel, and yet only part of the page changed. The UpdatePanel gathers the rendering from controls within the UpdatePanel and abandons most of the rest of the output. It also returns the updated hidden fields with things like ViewState that need to reflect the new state of the page. But the other controls’ output is not reflected in what the user sees.

Triggering Updates

The default behavior you saw for Listing 2-1 is for the UpdatePanel content to be refreshed when the child controls would normally trigger a postback. This is because the default value for the UpdateMode is Always, and the default value of the ChildrenAsTriggers property is true.

The UpdateMode property is of the type UpdatePanelUpdateMode. The two possible values are Always and Conditional. The default mode of Always means that the UpdatePanel will be refreshed anytime a child control initiates a postback. This corresponds to the ChildrenAsTriggers property’s default value of true. An UpdatePanel is triggered to refresh, and with the default settings, the child control’s postback triggers the update. In fact, if you set ChildrenAsTriggers to false without changing the UpdateMode value, you get the error shown in Figure 2-3. The following table summarizes the results of possible combinations of UpdateMode and ChildrenAsTriggers.

Image from book
Figure 2-3
Open table as spreadsheet
UpdateMode | ChildrenAsTriggers | Result
Always | false | Illegal parameters
Always | true | UpdatePanel refreshes if whole page refreshes or when a child control on the panel posts back
Conditional | false | UpdatePanel refreshes if whole page refreshes or when a trigger control outside the panel initiates a refresh
Conditional | true | UpdatePanel refreshes if whole page refreshes or when a child control on the panel posts back or when a trigger control outside the panel initiates a refresh

You can’t set ChildrenAsTriggers to false while the UpdateMode is set to Always. The two are contradictory, as you would be asking the UpdatePanel to refresh based on the child controls while simultaneously saying that the child controls should not trigger updates. The UpdateMode needs to be set to its other possibility: Conditional.

When the UpdateMode is Conditional and the ChildrenAsTriggers property is false, the UpdatePanel will not refresh unless the whole page is updated or when a control outside the UpdatePanel is defined as the trigger and initiates a postback. Listing 2-2 (Triggers.aspx) demonstrates this. I modified the previous listing to contain two UpdatePanel controls, each with a label that displays time retrieved from the server. The buttons are moved to the top of the page, outside of both of the UpdatePanel controls. The UpdateModel is set to Conditional. I also set ChildrenAsTriggers to false on one of the UpdatePanels, but in reality it is completely unnecessary in this example. There aren’t any child controls that would cause a postback, so the setting does not affect the behavior.

Listing 2-2
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server" language="C#" >
    protected override void OnLoad(EventArgs e) {
        base.OnLoad(e);
        string theTime = DateTime.Now.ToLongTimeString();
        for (int i = 0; i < 3; i++) {
            theTime += "<br />" + theTime;
        }
        time1.Text = theTime;
        time2.Text = theTime;
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Triggers</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
    <asp:Button ID="Button1" runat="server" Text="Update One" />
    <asp:Button ID="Button2" runat="server" Text="Update the Other" /><br /><br />

<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional" >
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
</Triggers>
<ContentTemplate>
    <div style="border-style:solid;background-color:gray;">

    <asp:Label runat="server" ID="time1"></asp:Label><br /><br />
    </div><br />
</ContentTemplate>
</asp:UpdatePanel>
<asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode="Conditional"
ChildrenAsTriggers="false" >
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Button2" EventName="Click" />
</Triggers>
<ContentTemplate>
    <div style="border-style:solid;background-color:gray;">
    <asp:Label runat="server" ID="time2"></asp:Label><br />
    </div><br />
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
Image from book

If I stopped here, the buttons would simply cause a full postback. There would be no partial updates. But inside the UpdatePanels, I have added a Triggers element with an AsyncPostBackTrigger control. This is where you define what controls should cause the UpdatePanel to be refreshed. The ControlID defines the source control and the EventName declares when to trigger.

Although demonstrating a dynamic effect on paper can be a challenge, Figure 2-4 shows the result.

Image from book
Figure 2-4

Although the majority of scenarios when using the UpdatePanel call for the update to be done asynchronously, in some cases you will want to have a full postback occur. You can make exceptions to the partial page update behavior by defining a PostBackTrigger. The event name does not need to be defined. Instead, you just provide the ControlID:

<Triggers>
<asp:PostBackTrigger ControlID="Button2" />
</Triggers>
Important

UpdatePanels can be nested inside one another. When the outer UpdatePanel is refreshed, the inner UpdatePanel will also be refreshed. But when the inner UpdatePanel is updated, the outer UpdatePanel is not affected. Even when the outer UpdatePanel has ChildrenAsTriggers set to true, an asynchronous update of the inner UpdatePanel will not cause the outer UpdatePanel to be refreshed. To have controls in a contained UpdatePanel cause a refresh, they have to be set explicitly as triggers on the outer UpdatePanel.

Calling the Update Method from the Server

So far, the examples have all been about controlling partial page updates from within the browser. The UpdatePanel also provides an Update method that allows a region of the page to be refreshed via code running on the server. During a partial page update, remember that the page runs normally and the UpdatePanel controls are just updating without the user seeing a full page postback. Any condition during the page execution can be reason to Update a portion of the page.

In Listing 2-3 (CallingUpdate.aspx) , the page code updating the time is changed to conditionally call Update on the second UpdatePanel when the first is being refreshed. The current time is stored in Session on each call. When the page executes, the current time is checked against the time stored previously in Session. If more than four seconds have elapsed, Update is called. The refreshed view of that portion of the page is then also updated when the call completes and the first UpdatePanel is updated.

Listing 2-3
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<script runat="server" language="C#">
    protected override void  OnLoad(EventArgs e) {
        object tempTime = Session["LastTime"];
        if (tempTime != null) {
            DateTime lastTime = (DateTime)tempTime;
            if (lastTime != null) {
                if (DateTime.Now > lastTime.AddSeconds(4)) {
                    UpdatePanel2.Update();
                    Session["lastTime"] = DateTime.Now;
               }
            }
        }
        else {
            Session["lastTime"] = DateTime.Now;
        }

     base.OnLoad(e);
        string theTime = DateTime.Now.ToLongTimeString();
        for(int i = 0; i < 3; i++) {
            theTime += "<br />" + theTime;
        }

        time1.Text = theTime;
        time2.Text = theTime;
    }
</script>
    <title>ASP.NET AJAX Calling Update</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
    <asp:Button ID="Button1" runat="server" Text="Update Conditionally" />

<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional" >
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
</Triggers>
<ContentTemplate>
    <div style="border-style:solid;background-color:gray;" >
    <asp:Label runat="server" ID="time1"></asp:Label><br /><br />
    </div><br />
</ContentTemplate>
</asp:UpdatePanel>

<asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode="Conditional" >
<ContentTemplate>
    <div style="border-style:solid;background-color:gray;" >
    <asp:Label runat="server" ID="time2"></asp:Label><br />
    </div><br />
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
Image from book
Tip

Although the UpdatePanel does not have a visible rendering of its own, it does wrap the rendering from the ContentTemplate in an HTML element. The default is to use a <div> element, but you can switch to use a <span> element instead. This is controlled through the RenderMode property. Setting the RenderMode to block results in the rendering being placed inside a div, and setting it to inline indicates that a span should be used.

The Partial Page Update Lifecycle

The page execution lifecycle is not altered by the partial rendering feature. The ScriptManager participates in the lifecycle to facilitate the partial page updates. It coordinates gathering the renderings from the UpdatePanels that need to be refreshed during an asynchronous post and carrying the hidden fields necessary to make the following post function correctly. Controls that modify the ViewState, even if they are not in the UpdatePanel being affected, do not have to take any special action to ensure the change is available in subsequent requests. Event validation, cross page posting, and ASP.NET’s ability to maintain the scroll position all continue to work when using the UpdatePanel.

When using the UpdatePanel, it is key to remember that the page lifecycle is the same. It’s nice that no special pains are required to start taking advantage of partial page updates, but don’t forget that the overhead on the server is the same and that the traffic between browser and server can still be significant. For example, if you have a GridView or other data control on the page that is making use of ViewState to avoid trips to the database, that ViewState will be carried with each request even when the data is not being updated. As this ViewState baggage can slow down partial page updates, you should carefully consider which controls need to have ViewState enabled, and you should disable ViewState on any particular controls where it isn’t needed.

There are several ways to determine whether your code is executing in the context of a partial page update. For the control author to find information about the current state, he can walk up the control tree to find if the controls are contained within an UpdatePanel and query the IsInPartialRendering property. This property reveals if the UpdatePanel is rendering back to the browser with asynchronous updates in order to take custom actions if desired. For the page author who just wants to know if the page is executing as the result of an UpdatePanel initiated asynchronous post, the ScriptManager has the information. Check the IsInAsyncPostBack property to make the determination:

if (ScriptManager1.IsInAsyncPostBack)

UpdatePanel Cautions and Complexities

Not every control will work well with the UpdatePanel. The ScriptManager provides new versions of APIs for control authors to register JavaScript for use in the browser. Complex controls that make heavy use of dynamically registered script won’t work correctly until they are updated to switch from using the ClientScriptManager of ASP.NET 2.0 to using the ScriptManager included with ASP.NET AJAX. For example, the ASP.NET Menu and TreeView controls are not fully compatible with the UpdatePanel in the 1.0 release. Those controls both already do out-of-band communication with the server, so there is less impetus to put them inside an UpdatePanel.

The validation controls also register script in a way that is incompatible with the UpdatePanel. An update is available from Microsoft to correct the problem. You can also disable the client script for the validator controls and then use them in an UpdatePanel:

<asp:RequiredFieldValidator EnableClientScript="false" runat="server"
ControlToValidate="TextBox1" />

With EnableClientScript set to false, the validator controls no longer perform any client-side validation. Instead, the errors must be caught on the server explicitly by your code. (By the way, because the client can disable or modify JavaScript running in the browser, you should always validate incoming values on the server anyway.) More details as well as the source code for an updated set of validator controls is available on the ASP.NET Forums at http://forums.asp.net/thread/1545781.aspx.

Another complication with the set of supported controls for use with the UpdatePanel is Web Parts. Until an update is made to Web Parts and made available by Microsoft, you cannot simply place a Web Part inside an UpdatePanel and have it refresh asynchronously.

The GridView and DetailsView controls already have a feature for using client callbacks to paging and sorting. It is not enabled by default, but if the feature is enabled, these controls cannot be used inside an UpdatePanel. The FileUpload control also can’t submit files as part of an asynchronous postback. Instead, to have an UpdatePanel contain a FileUpload, the control that triggers the upload should be configured to cause a full postback using a PostBackTrigger:

<asp:UpdatePanel ID="UpdatePanel" runat="server" UpdateMode="Conditional" >
<ContentTemplate>
    <asp:FileUpload runat="server" ID="fileUpload" />
    <asp:Button runat="server" Text="Upload" ID="UploadButton" />
</ContentTemplate>
<Triggers>
<asp:PostBackTrigger ControlID="UploadButton" />
</Triggers>
</asp:UpdatePanel>

The asp:substitution control presents complications that haven’t been addressed with the ASP.NET AJAX 1.0 release and prevents the substitution from working inside an UpdatePanel. The 1.0 release is built on top of ASP.NET 2.0, and given the complexity of the caching infrastructure, it may require changes to the core to enable the Substitution control to work with partial page rendering.

And finally, because of the way the ScriptManager extracts the contents needed for the partial page updates in the 1.0 release, calling Response.Write during the execution of an asynchronous postback will result in an error. This is expected to be addressed in a future release or update to the 1.0 release.


UpdateProgress Control

As asynchronous updates are happening, you may want to let the user know that there is work pending. Otherwise, any time the response is not instantaneous, the user will be left wondering if their action has been ignored. With no visual acknowledgement of the input that is causing asynchronous work, the experience can be worse than the screen going blank waiting for a response from a full postback. Therefore, Microsoft created the UpdateProgress control to make it easy to provide visual feedback to the user to indicate that the update is being processed. This is something like a “busy” hourglass cursor in Windows applications, but this does not involve changing the cursor.

Listing 2-4 (UpdateProgress.aspx.cs) is a code-behind file that again updates the rendering with the current time on the server. To make it appear as though the page is doing some heavy lifting that the user might need to wait for, I have added a Sleep method call so that the content is not returned instantly. Sleep simulates a short time lag that may result from a query to a database, or any other server-side processing.

Listing 2-4
Image from book

using System;
using System.Web.UI;

public partial class UpdateProgress : System.Web.UI.Page
{
    protected override void  OnLoad(EventArgs e) {
        System.Threading.Thread.Sleep(4000);
        base.OnLoad(e);
        string theTime = DateTime.Now.ToLongTimeString();

        for(int i = 0; i < 3; i++) {
            theTime += "<br />" + theTime;
        }
        time1.Text = theTime;
    }
}
Image from book

The page markup has an UpdatePanel where the time1 label is and will be updated. It is shown in Listing 2-5 (UpdateProgress.aspx). Besides the UpdatePanel, there is the addition of an UpdateProgress control that will be displayed while the asynchronous postback is underway.

Listing 2-5
Image from book
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="UpdateProgress.aspx.cs"
Inherits="UpdateProgress" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Update Progress</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>

some page content<br />
<asp:UpdateProgress ID="UpdateProgress1" runat="server" DynamicLayout="true"
AssociatedUpdatePanelID="UpdatePanel1" >
<ProgressTemplate>
<img src=progress.gif />
</ProgressTemplate>
</asp:UpdateProgress>
more page content<br />
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
    <ContentTemplate>
        <div style="border-style:solid;background-color:gray;">
        <asp:Button ID="Button1" runat="server" Text="Update" /><br /><br />
        <asp:Label runat="server" ID="time1"></asp:Label><br />
        </div><br />
    </ContentTemplate>
</asp:UpdatePanel><br />
</div>
</form>
</body>
</html>
Image from book

Like the UpdatePanel control, the UpdateProgress control has a template that holds the content that it will display. In this example, the ProgressTemplate contains two image tags. Initially, the progress.gif images are not visible. But while the request is pending they are displayed between the top two lines of text, as shown in Figure 2-5.

Image from book
Figure 2-5

I just used the default values for the UpdateProgress control, which results in a 500-millisecond delay before the ProgressTemplate is shown. If it were to be displayed instantly with data returned quickly, the result would have been unacceptable flicker and flash that the UpdatePanel was supposed to avoid in the first place. To avoid this flicker in the event of a quick data fetch, you can configure the UpdateProgress control using the DisplayAfter property, which takes an argument to indicate how long it should delay in milliseconds. If an asynchronous update takes longer than the DisplayAfter threshold, the contents of the UpdateProgress are displayed:

<asp:UpdateProgress ID="UpdateProgress1" runat="server" DisplayAfter=800 >

You are probably less likely to change the DisplayAfter default than the value of DynamicLayout. The default behavior is not to allocate any space for the contents of the UpdateProgress. Figure 2-6 shows the page when no update is being made. Notice that no space is provided for the progress.gif images seen in Figure 2-5. Instead, room is made for them only when needed. As illustrated in Figure 2-5, the rest of the page was shifted down slightly to make room for the images. This can be disconcerting to users to see the page layout change for a short time. To preallocate the space and avoid a change in the layout, set DynamicLayout to false:

<asp:UpdateProgress ID="UpdateProgress1" runat="server" DynamicLayout="false"  >
Image from book
Figure 2-6

The layout options will really depend on the nature of your application. The UpdateProgress will display for every asynchronous postback unless you assign it specifically to a single UpdatePanel using the AssociatedUpdatePanelID property:

<asp:UpdateProgress ID="UpdateProgress1" runat="server"
AssociatedUpdatePanelID="UpdatePanel1" >

One common approach is to use a CSS class with the UpdateProgress that controls its positioning so that the ProgressTemplate contents are displayed as a layer right over the top of the UpdatePanel being affected. The following is an example set of styles that, when associated with a div for an UpdateProgress will produce a small square on the screen to contain whatever images or other information you want to display while an update is occurring.

<style type="text/css">
.progress
{
    border:solid;
    background-color: white;
    position: absolute;
    top: 180px;
    left: 180px;
    width: 60px;
    height: 60px;
    padding-left:40px;
    padding-top:40px;
    z-index:1
}
</style>


Automatic Updates with the Timer

The question that immediately follows when someone sees what that they can easily update parts of the page asynchronously using a trigger is “How can I make it update automatically?” The Timer control makes this easy! Instead of requiring the user to perform an action in order to trigger updates, the Timer control will initiate the postback automatically. Listing 2-6 (Timer.aspx) shows the primary controlling property; the interval is set to 1000 milliseconds. The Timer will initiate a postback every second.

Listing 2-6
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<script language="C#" runat="server">
    protected override void  OnLoad(EventArgs e) {
        base.OnLoad(e);
        System.Threading.Thread.Sleep(100);
        string theTime = DateTime.Now.ToLongTimeString();
        for(int i = 0; i < 3; i++) {
            theTime += "<br />" + theTime;
        }
        time1.Text = theTime;
        time2.Text = theTime;
    }
</script>

    <title>Timer</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>

<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Always">
    <ContentTemplate>
        <div style="border-style:solid;background-color:gray;">
        <asp:Label runat="server" ID="time1"></asp:Label><br />
        </div><br />
                <asp:Timer ID="timer1" runat="server" Interval="1000"   >
        </asp:Timer>
    </ContentTemplate>
</asp:UpdatePanel><br />

</div>
</form>
</body>
</html>
Image from book

The Timer can be placed directly inside the UpdatePanel to act as a child control trigger. It can also be placed anywhere and configured as the AsyncPostBackTrigger explicitly. A single timer can be the trigger for multiple UpdatePanels.

Listing 2-7 (SingleTimer.aspx) shows a modification of a previous example that has two UpdatePanels, and this time we’ll add a Timer. The first UpdatePanel has Always for the UpdateMode, so the Timer inside it will make it update on every Tick event. The second UpdatePanel set the UpdateMode to conditional and therefore requires that a trigger be defined. So it is wired up to the Timer from the first UpdatePanel.

Listing 2-7
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<script language="C#" runat="server">
    protected override void  OnLoad(EventArgs e) {
        base.OnLoad(e);
        System.Threading.Thread.Sleep(100);
        string theTime = DateTime.Now.ToLongTimeString();
        for(int i = 0; i < 3; i++) {
            theTime += "<br />" + theTime;
        }
        time1.Text = theTime;
        time2.Text = theTime;
    }
</script>

    <title>Timer</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>

<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Always">

<ContentTemplate>

<asp:Timer ID="timer1" runat="server" Interval="1000"   ></asp:Timer>
    <div style="border-style:solid;background-color:gray;">
    <asp:Label runat="server" ID="time1"></asp:Label><br />
    </div><br />
</ContentTemplate>
</asp:UpdatePanel><br />

<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="timer1" EventName="Tick" />
</Triggers>
<ContentTemplate>
    <div style="border-style:solid;background-color:gray;">
    <asp:Label runat="server" ID="time2"></asp:Label><br />
    </div><br />
</ContentTemplate>
</asp:UpdatePanel>

</div>
</form>
</body>
</html>
Image from book

And, of course, you can also place the Timer outside the UpdatePanel to trigger a full postback at regular intervals. I picked an interval of one second to demonstrate that a one-second interval does not mean the refresh will occur every second. The Timer instructs the browser to trigger an event after one second. This accuracy is determined by the granularity of the specific browser, so there will normally be a small additional delay. Once the Timer is signaled by the browser, it can raise its Tick event and the refresh will occur. You can see that the combination of different interactions will result in the refresh occurring at approximately one second, but it is not exact.


Managing Page Requests

Every .aspx page that uses ASP.NET AJAX must contain a ScriptManager control that manages scripts for rendering to the client. And it oversees the processing of partial page updates to get the various UpdatePanel controls refreshed appropriately. The ScriptManager and UpdatePanel have a counterpart in JavaScript to manage this behavior. The PageRequestManager is the central point of interaction for the client side.

There is a single instance of the PageRequestManager for each page, which can be retrieved through the getInstance static function. Listing 2-8 (ClientLifecycle.aspx) demonstrates working with it. The PageRequestManager is retrieved and then used to add handlers for the pageLoaded and initializeRequest events. The event handlers in this example just use the alert function to show that they have been invoked.

Listing 2-8
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>PageRequestManager</title>
<script language="C#" runat="server">
    protected override void OnLoad(EventArgs e) {
        theTime.Text = DateTime.Now.ToLongTimeString();
    }
</script>

</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<script type="text/javascript">
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_pageLoaded(pageLoaded);
prm.add_initializeRequest(initializeRequest);

function initializeRequest(sender, eventArgs) {
    alert("initializeRequest is only called for asynchronous requests");
}

function pageLoaded(sender, eventArgs) {
    alert("pageLoaded is called for synchronous and asynchronous requests");
}
</script>
<div style="border-style:solid">
<asp:UpdatePanel runat="server">
<ContentTemplate>
<br />&nbsp;<asp:Label runat="server" ID="theTime"></asp:Label><br />
<asp:Button runat="server" Text="Partial Update" /><br /><br />
</ContentTemplate>
</asp:UpdatePanel>
</div><br />

<asp:Button runat="server" Text="Regular PostBack" />
</form>
</body>
</html>
Image from book

The Request Lifecycle

When the page is first retrieved, the pageLoaded event is fired. As the message in Figure 2-7 shows, the pageLoaded event occurs for all requests, synchronous and asynchronous. When the request for the partial page request is started by clicking the button in the UpdatePanel, the initializeRequest alert will be shown. This event does not occur for the full postback triggered by the button outside the UpdatePanel. It is part of the asynchronous lifecycle implemented by the PageRequestManager.

Image from book
Figure 2-7

The lifecycle provided by the PageRequestManager for asynchronous requests includes five events:

  1. initializeRequest

  2. beginRequest

  3. pageLoading

  4. pageLoaded

  5. endRequest

The potentially confusing thing about the series of events is that they don’t fire all of the time. Requests can be canceled or preempted by another request. Listing 2-9 (LifecycleCounts.aspx) sets up counters that are incremented for each time the event handlers are invoked. It also increments a counter on the server for every time the page executes. The count is displayed inside an UpdatePanel. The page sleeps briefly so that the behavior of initiating a request while another is pending can be seen. There is also a button for displaying the current values.

Listing 2-9
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>PageRequestManager</title>
<script language="C#" runat="server">
    protected override void OnLoad(EventArgs e) {
        if (Session["count"] == null) {
            Session["count"] = 1;
        }
        else {
            Session["count"] = ((int)Session["count"]) + 1;
        }
        System.Threading.Thread.Sleep(1000);
        theCount.Text = "count = " + Session["count"].ToString();
    }
</script>

</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<script type="text/javascript">
var initializeCount = 0;
var beginCount = 0;
var loadingCount = 0;
var loadedCount = 0;
var endCount = 0;

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_initializeRequest(initializeRequest);
prm.add_beginRequest(beginRequest);
prm.add_pageLoading(pageLoading);
prm.add_pageLoaded(pageLoaded);
prm.add_endRequest(endRequest);

function initializeRequest(sender, initializeRequestEventArgs) {
    initializeCount = initializeCount + 1;
}

function beginRequest(sender, beginRequestEventArgs) {
    beginCount = beginCount + 1;
}

function pageLoading(sender, pageLoadingEventArgs) {
    loadingCount = loadingCount + 1;
}

function pageLoaded(sender, pageLoadedEventArgs) {
    loadedCount = loadedCount + 1;
}

function endRequest(sender, endRequestEventArgs) {
    endCount = endCount + 1;
}

function showCounts() {
    var message = "initialized = " + initializeCount +
            "\r\nbegin = " + beginCount +
            "\r\nloading = " + loadingCount +
            "\r\nloaded = " + loadedCount +
            "\r\nend = " + endCount;
    alert(message);
}
</script>
<div style="border-style:solid">
<asp:UpdatePanel runat="server">
<ContentTemplate>
<br />&nbsp;<asp:Label runat="server" ID="theCount"></asp:Label><br />
<asp:Button runat="server" Text="Refresh" ID="RefreshButton" /><br /><br />
</ContentTemplate>
</asp:UpdatePanel>
</div><br />

<input type="button"  onclick="showCounts()" value="Show Counts" />
</form>
</body>
</html>
Image from book

After the page is first run, it shows one page execution and one pageLoaded. The other counts are zero. If you click the Refresh button and wait, all of the counts are incremented by one. But if you click the button several times quickly, the counts begin to change out of synch with each other. Notice in Figure 2-7 that the page execution count of 12 corresponds to the count of the pageLoaded counter. This count is one ahead of the beginRequest, loadingPage, and endRequest counters. However, the initializedRequest counter is now much higher. As I clicked the button faster than the request could be initiated, the previous start would be canceled in favor of the newer request. At that point, the multiple starts are essentially seen as simultaneous, and only one is advanced to the beginRequest event.

Canceling a Request

The initializeRequest event alerts you to the fact that a request is about to begin and gives you access to the element responsible for initiating the request. You can examine the state of things and explicitly cancel the request here before it even begins based on other factors. For example, you can query the PageRequestManager and see that an asynchronous postback is already underway:

function initializeRequest(sender, initializeRequestEventArgs) {
    var prm = Sys.WebForms.PageRequestManager.getInstance();
    if(prm.get_isInAsyncPostBack() === true) {
        initializeRequestEventArgs.set_cancel(true);
        alert("canceling event from " +
initializeRequestEventArgs.get_postBackElement().id);
    }
    initializeCount = initializeCount + 1;
}

That would prevent the new request from starting, but you also might want to abort a request after it has started. Suppose you included a Cancel button in your page:

<input type="button" onclick="cancelRequest()" value="Cancel Request" />

In the onClick handler, you could then abort a request that is already underway but not yet completed:

function cancelRequest() {
    var prm = Sys.WebForms.PageRequestManager.getInstance();
    if(prm.get_isInAsyncPostBack() === true) {
        prm.abortPostBack();
    }
}

You can see in Figure 2-8 that the count of calls to the different event handlers can get out of synch. The number of times the page executed is no longer the same as the number of times the page was loaded. Requests were canceled after the page execution was already started. The endRequest event is still called for every beginRequest. And once the pageLoading event started, the load finished and the pageLoaded event was called.

Image from book
Figure 2-8

You can count on getting an endRequest event if the beginRequest event occurs, even if the request is canceled. But, as you have seen, you can’t expect each event for every request initialized. Other factors will come into play that will also skew the lifecycle.

Detecting Errors

Not to sound pessimistic, but just when you think everything is going great, inevitably something goes wrong. The EndRequestEventArgs includes an error property to allow you to detect and react to errors that have happened during asynchronous requests. The default behavior is to display an error, but you may want to change this to save some user state and navigate to your own error page or to provide a recovery path unique to your application.

To demonstrate, I simply throw a NotImplementedException for postbacks on the server:

<script language="C#" runat="server">
    protected override void OnLoad(EventArgs e) {
        if(IsPostBack) {
            throw new NotImplementedException("something terrible has happened");
        }
    }
</script>

The endRequest handler then displays the error description and sets the errorHandled property so the PageRequestManager won’t react to it any further:

function endRequest(sender, endRequestEventArgs) {
    var theError = endRequestEventArgs.get_error();
    if(theError !== null) {
        alert(theError.description);
        endRequestEventArgs.set_errorHandled(true);
    }
}

The EndRequestEventHandler also exposes a property to indicate whether or not the request was aborted before while it was being processed:

function endRequest(sender, endRequestEventArgs) {
    if(endRequestEventArgs.get_aborted()) {
        alert("request was aborted");
    }
}

Working with Updates

Part of the complexity of partial page updates comes in establishing and maintaining relationships between elements on the page as the page evolves and as parts are updated independently. Of course, in real applications, you will have event handlers attached to DHTML DOM (Document Object Model) elements within an UpdatePanel that affect other parts of the page. When an asynchronous postback occurs, the event hookup will be lost and must be reestablished.

I extended the idea of displaying the server time in an UpdatePanel to have it in two separate UpdatePanels: panel1 and panel2.

<asp:UpdatePanel runat="server" ID="panel1" UpdateMode="Conditional">
<ContentTemplate>
Panel One
<asp:Label runat="server" ID="theTime"></asp:Label><br />
<asp:Button runat="server" Text="Refresh" ID="RefreshButton" /><br /><br />
</ContentTemplate>
</asp:UpdatePanel>
</div><br />

<div style="border-style:solid">
<asp:UpdatePanel runat="server" ID="panel2" UpdateMode="Conditional">
<ContentTemplate>
Panel Two
<asp:Label runat="server" ID="theTime2"></asp:Label><br />
<asp:Button runat="server" Text="Refresh" ID="RefreshButton2" /><br /><br />
</ContentTemplate>
</asp:UpdatePanel>

The time1 and time2 labels are updated each time the page is executed on the server:

<script language="C#" runat="server">
    protected override void OnLoad(EventArgs e) {
        string text = DateTime.Now.ToLongTimeString();
        theTime.Text = text;
        theTime2.Text = text;
    }
</script>

Then I added a span elsewhere on the page that should reflect the time as it is in panel1:

The Time from Panel One is: <asp:Label runat="server" ID="theDuplicate" /><br />

If the dependency were really as simple as getting one string and copying it over as I am doing here, it wouldn’t merit any extra work. I would just copy it over in a pageLoaded event handler and be done with it. In a more realistic example, the dependencies would probably be more labor intensive. Instead of blindly doing the work each time any part of the page is updated, you would want to do the work only when necessary. In this case, that would be just when panel1 was changing. Instead of putting the code directly in the pageLoaded event handler, it goes in a separate function.

function hookupPanelOne() {
 $get('<%= theDuplicate.ClientID %>').innerHTML =
$get('<%= theTime.ClientID %>').innerHTML;
}

The $get syntax is a shortcut for locating the item in the browser’s DOM. (More details on the Microsoft AJAX Library are in Chapter 4.) To tell when the hookupPanelOne function needs to be called, I examine which UpdatePanels are being created and which are being updated in the pageLoaded event han-dler. The PageLoadedEventArgs provides property accessors that return arrays of the newly created and updated UpdatePanels.

function pageLoaded(sender, pageLoadedEventArgs) {
    var panelsCreated = pageLoadedEventArgs.get_panelsCreated();
    for(var i = 0; i < panelsCreated.length; i++) {
        if(panelsCreated[i].id === "panel1") {
            hookupPanelOne();
        }
    }

    var panelsUpdated = pageLoadedEventArgs.get_panelsUpdated();
    for(var i = 0; i < panelsUpdated.length; i++) {
        if(panelsUpdated[i].id === "panel1") {
            hookupPanelOne();
        }
    }
}

Yes, this looks like a lot of overhead for a simple example, but if you had cascading dependencies or were making additional Web Service calls as the result of an UpdatePanel being refreshed, you could save extra work for the browser and the server by updating the dependency only when it’s necessary. Putting it all together results in the page shown in Figure 2-9. The panels can update independently, and the Duplicate span is changed as a function of updates to the first panel only.

Image from book
Figure 2-9


AJAX and Accessibility

Accessibility in this section is used to describe making a web page friendly to screen reader software for users with vision impairments. Accessibility is a challenge with traditional web applications and becomes even more complicated with asynchronous updates. The strictest guidelines dictate no use of JavaScript whatsoever, which rules out everything shown so far in this chapter. Much of ASP.NET 2.0 relies on JavaScript for initiating postbacks. Given those caveats, there are some practical considerations that make for an accessible user experience.

The JAWS screen reader seems to be the most popular and works well despite the use of JavaScript. And appropriate use of column headers and labels works well to make sites accessible. But when you start updating portions of the screen asynchronously, you need to look at ways to let the screen reader know what has happened. One approach commonly used for accessibility is to use an iframe within a page where dynamic updates are occurring. Navigating the iframe itself is detected by screen readers so you can assert more control over what content updates are obvious. It is not a perfect solution as it returns the flash and flicker.


Summary

The UpdatePanel is a simple and flexible control for adding partial page updates to an ASP.NET application. You can take an existing application and identify parts of a page that can be updated independently, bypassing the click-pause-flash pattern that annoys users in web applications. The ViewState and other hidden fields are updated automatically as part of the ScriptManager’s coordination of UpdatePanels on the page.

The default behavior is to have every action that would have triggered a postback from a child control of the UpdatePanel cause a partial page update. The children controls are treated as triggers; thus, the ChildrenAsTriggers property is true by default. You can also configure Triggers to be events on controls elsewhere on the page.

ASP.NET AJAX is an extension to ASP.NET 2.0. Some limitations will require updates to the underlying framework in order for controls that already exist to work with the UpdatePanel. The controls need to migrate from using the ClientScriptManager to the new ScriptManager included as part of ASP.NET AJAX. Some of the built-in controls, like the Validators, have been updated already. Other controls, like the Substitution and WebParts controls, will require more extensive changes in a future release of the .NET Framework before they are compatible.

The PageRequestManager provides a whole lifecycle of events in the browser that allows you to control how asynchronous requests occur. You can cancel requests before they start, abort them while they are in flight, and react to any errors that happen while they are being processed.

The UpdateProgress control makes it easy to let the end user know that an asynchronous request is being processed. Without it, a long-running request can leave the user feeling that his actions are being ignored. You can have an UpdateProgress control associated with each UpdatePanel or a single UpdateProgress associated with multiple UpdatePanels.

You can easily update parts of the page out-of-band based on the user clicking a button, and you can also update parts of the page automatically. The Timer control makes it simple to define an interval to have parts of the page refreshed without user intervention.

As soon as you start initiating asynchronous requests, the need may arise to cancel requests before they start, or abort them before they complete. The PageRequestManager provides events that give you fine-grained control over the asynchronous requests. And when errors occur during the page request, you can detect them and react accordingly.

The UpdatePanel and PageRequestManager work together with the help of the ScriptManager to provide a platform for developing a more responsive and intuitive user experience. The extensibility points make it easy to integrate partial page rendering into existing complex applications.


Chapter 3: JavaScript for the ASP.NET Developer

Overview

As an ASP.NET developer, you are used to writing code using a high-level language such as C# or VB.NET. Visual Studio provides Intellisense that makes coding easier. There is rich support for debugging the code, and you get warnings and errors about many things during compilation. Visual Studio will even background-compile your code and give you updated warnings as you go to help you find problems without needing to perform an explicit compilation. The .NET Framework provides a comprehensive set of class libraries that can be used from any .NET-com-patible language and helps to simplify complex programming tasks. Writing JavaScript, however, is somewhat different.

In this chapter, I introduce JavaScript with a bias toward the ASP.NET developer. You will see how JavaScript development is different from what you are used to with .NET languages. In addition, you will see how the browser gets JavaScript. I introduce the basics of the language and explain the closure and prototype aspects of JavaScript coding, along with some techniques to apply for writing faster and more efficient JavaScript. If you look at the code of the Microsoft AJAX Library, you will see that the ASP.NET team uses these tactics in their JavaScript development efforts. If you are already familiar with JavaScript, you may want to skip the first part of this chapter, where I introduce JavaScript, and instead look at some approaches for improving JavaScript code.

Tip

This chapter is not meant to be an exhaustive treatment of JavaScript. There are comprehensive books and online tutorials that can take you through the language step by step and highlight differences among the minor version releases from Microsoft, Netscape, and Mozilla. I recommend Professional JavaScript for Web Developers, by Nicholas C. Zakas (2005), or The JavaScript Bible, by Danny Goodman and Michael Morrison (2007), both from Wiley Publishing. The goal here is to highlight the key facts and interesting aspects of the language that may be unfamiliar to someone coming from C# or VB.NET and help you to skip some of the pitfalls that hinder development efforts or slow down JavaScript code.


Using JavaScript on the Client

The JavaScript language can be used independently of the browser. There are JavaScript interpreters for managing operating system components, interacting with COM objects, and automating server administration tasks. To use JavaScript in the browser, there are a few considerations you should be aware of. First, you need to manage getting the script to the browser for a page effectively. As the amount of JavaScript you use grows, you will want to make use of the browser’s ability to cache scripts. You will also need to be aware of how the user’s interaction with a page while background operations are performed will affect their perceived performance of a page. In some conditions you want them to know that they are waiting for an action to be performed so they don’t become frustrated, and in other cases you want to ensure that the page is responsive while something happens transparently in the background. JavaScript resources can also be embedded directly in a DLL and don’t have to be deployed as text files on disk as you might be accustomed to doing.

Getting JavaScript to the Browser

Before diving into the fundamentals of the language, you should understand how JavaScript code gets to the browser as part of a web application. The HTML script tag is used to separate the script code from the markup so the browser will be able to distinguish the difference. Listing 3-1 shows the two methods for including JavaScript in a page. In the first example, the script code is included directly in the page within an HTML script element. The second example (the last line) defines the script code in a separate file and uses the src attribute of the script element in the page to provide the URL that the browser should use to retrieve the code.

Listing 3-1
Image from book

<script type="text/javascript">
alert("greetings");
</script>

<script type="text/javascript" src="scripts/code.js" ></script>
Image from book

It used to be common to include language ="javascript" in the script tag as well, but the language attribute has been deprecated in the latest HTML standards, although it is still accepted by most browsers at the present time. Multiple script tags can be included in the HTML source so you can have many different sections of script code in a single page. Any code that is not defined within a script function will be executed immediately when the page loads. The W3C standard includes the defer attribute to instruct the browser that it need not delay rendering of the HTML while the script is downloaded or parsed. This can be useful when you know that a particular piece of script code won’t need to run until after the page has been completely rendered. Be aware, however, that users will start interacting with the page as soon as it is rendered. Users don’t like waiting for page loads, and they certainly aren’t watching the animated browser icon to see if the browser is still busy loading part of the page that they cannot see. Therefore, the defer attribute is not commonly used.

If you use defer and the user performs actions in a partially rendered page, the code that tries to execute something from a deferred script before it is has finished loading will generate errors for the user. This is baffling to users, and the only recovery action is to load the page again. The error message they get is usually something vague about a function or variable being undefined. They don’t have any way to know that the undefined something may not have loaded yet, so they may even repeat the same process and get the same error. The safest approach is to include script references earlier in the page whenever possible and avoid using deferred loading. This will make it more likely that the browser has your script code when the user starts interacting with the page. Of course, even this isn’t a guarantee. To protect yourself from errors caused by early user interaction with a page, you can include a top-level piece of code to disable elements that might depend on load order. This way, you can avoid triggering the use of code that is still being fetched. For example, you can disable a Submit button in an HTML form to prevent the user from clicking it and needing some associated validation scripts before they are available.

Perceived Performance

If you allow users to interact with a page before all the script code has been downloaded, it will appear to them that the page is ready to be used. By temporarily disabling the postback, you can ensure safety and also allow users to enter their data on the page while the scripts continue loading in the background.

In addition to the perceived performance advantage, some applications also temporarily disable elements of the form that cause a postback in order to avoid a complication with a security feature of ASP.NET. Some controls can be registered for validation, and ASP.NET will round-trip the set of valid values set for that control. The possible values are hashed (a cryptographic signature is calculated on them and later validated after postback) and round-tripped along with the values so that the server can detect any attempt by malicious code to submit something other than the valid list of values. Because the set of authentic values has to be collected while the page is being rendered and may not be fully assembled until after the form has been fully rendered, the hashed data is included after the closing form ele-ment. When a specific web form is fairly large or complex, the set of validation values can also be large, so it is possible for the user to interact with the form and submit it before the browser receives the validation data. The browser would then be unable to submit the set of valid values back to the server, and the user will see an error when the page is posted back on the server and the validation values are missing. So, to prevent this scenario, you can disable the postback until you’re sure that the browser has received all of the validation values.

In Listing 3-2 (from Enable.htm), the Submit button is disabled initially in the HTML markup. The script element contains a function to enable the Submit button along with a line of script for attaching the function to the window’s onload event. Thus, the button will be disabled initially and then enabled after the whole page has been loaded.

Listing 3-2
Image from book

<html xmlns=http://www.w3.org/1999/xhtml >
<head>
<title>Example</title>
</head>
<body>
<form action="enable.htm" id="theForm">
<script type="text/javascript">
function enableSubmit() {
    var elt = document.getElementById('submitButton');
    alert("enabling form now");
    elt.disabled = false;
}
window.onload = enableSubmit;
</script>
<input type="submit" id="submitButton" value="go" disabled="disabled"  />
</form>
</body>
</html>
Image from book

Notice that I only disabled the Submit button. My first inclination would be to disable the entire form and have everything in it disabled, but that would not be portable to all the browsers. In a real application with several elements that might cause a postback, you would have to initially disable all of them and then enable them during the onload event.

There isn’t a limit to the number of script tags allowed on a page, but browsers typically open only one or two channels back to the server for scripts and images, so it appears as though the page is loading slowly. You can improve the perceived performance of page loading by using several script tags to contain your JavaScript code so that the browser can download multiple scripts simultaneously. This may not complete the overall load any faster, but it can seem faster by allowing the user to start interacting with the page earlier.

Script Resources

One of the longstanding difficulties with the deployment of web applications to web servers is ensuring that the right versions of all the miscellaneous files are deployed. ASP.NET 2.0 added the ability to include JavaScript code as a text resource embedded in the compiled dll for an application. This allows you to create a web server control that includes the script files internally and can be deployed with only configuration entries and a single dll, ensuring that the correct script files will always be sent to the user and will not have to be maintained in a folder on the server! ASP.NET will then field requests for those script resources via its built-in Web Resource handler. If you view the source for just about any .aspx page, you will see references to WebResource.axd followed by querystring parameters used to retrieve the appropriate embedded resource. At first glance, it looks like a bunch of garbage, but if you extract one of those URL querystrings from a typical page’s source and paste it into your browser’s address window to request it separately, you will find that the proper resource is returned.

http://www.mysite.com/WebResource.axd?d=3843647524---
HJKAeoedaslathlmhueeuyr&amp;t=111205042031232968

The Web Resource handler is used for more than just JavaScript files; it can also be used for image files. Since the first version of ASP.NET did not have this functionality, some developers modified the standard ASP.NET JavaScript files themselves to obtain certain types of custom behaviors. To maintain functionality for those customizations when upgrading to the next release, ASP.NET will check for the existence of those external script files in an application and use them instead of the ones provided by Microsoft in System.Web.dll. However, this WebResource mechanism can still be used by your own web controls, regardless of whether it is used for the standard ASP.NET scripts used by all pages.

Image from book
Naming and Casing in JavaScript

Switching back and forth between languages can be a challenge. VB.NET is not case sensitive, but C# is. JavaScript also is. The .NET Framework Design guidelines advocate variable and API naming conventions that are different than what is used in JavaScript. The convention in .NET development is to use camel-casing for member variables, with leading underscores for privates. Pascal casing is used for public members. When writing JavaScript, camel-casing is employed for public members. Developers have come to rely on these conventions so it pays to be attentive to these details although it can be confusing at first, especially if you are switching often.

Image from book


Variable Types in JavaScript

JavaScript is often characterized as having no data types. Everything is just an object. This is basically true; however, some primitive types can be assigned to variables, and the variables can pick up those types (this is similar to variants for those of you with COM experience). Numbers, strings, Booleans, and functions are the primitive types in JavaScript. They are the building blocks for all objects in JavaScript. Even the higher order “types” that are predefined like arrays and regular expressions are just collections of the basic types.

In Listing 3-3 (boolean.aspx), I have declared a few variables, assigned them integer values, and then displayed true and false after treating them as Boolean values. The key thing to note is that JavaScript follows the C-style convention of treating 0 as false and everything else as true. When the page is run, the result is to display ‘true’, ‘false’, and ‘true’ for one, zero, and negative one.

Listing 3-3
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Booleans</title>
    <script type="text/javascript">
    var myOne = 1;
    var myZero = 0;
    var myNegativeOne = -1;
    var myTrue = true;
    var myFalse = false;

    if(myOne) {
        alert(true);
    }
    if(!myZero) {
        alert(false);
    }
    if(myNegativeOne) {
        alert(true);
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    </div>
    </form>
</body>
</html>
Image from book

To the .NET developer, this set of basic types is pretty straightforward. Strings, numbers, and Booleans are fundamental to all programming tasks, so having them as basic types seems natural. But you can’t anchor a variable to specific data types when they are declared in JavaScript, so you have to use greater care when writing code. You won’t get an error if you assign a string to a number variable, since the variable isn’t locked down to holding only numbers. Still, this can generate an odd error at runtime when you try to access that variable as a number. This is a common problem of all typeless interpreted languages.

Without the ability to tie variables to specific types, you lack static type safety. The language is not compiled; it is interpreted. Types can be created at runtime and dynamically altered during script execution. In other languages, you must declare a type for a variable, and if the variable is used to refer to an object of a different type, you will get a compilation error. Another option in C++ and .NET languages is to carefully coerce variables from one type to another. This is not the case in JavaScript. There is only one type of variable declaration, and no type specifier is permitted. Any variable can refer to any kind of object at any time. In Listing 3-4 (from variablesTypes.aspx), a single variable is declared and then assigned to the fundamental types used in the previous example. I use the typeof operator to show that the type reference changes while the script is being executed.

Listing 3-4
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Code Test Page</title>
</head>
<body>

<script type="text/javascript">
var variable;
alert(typeof(variable));//refers to something undefined;

variable = 1;// refers to a number
alert(typeof(variable));

variable = true; //refers to a Boolean
alert(typeof(variable));

variable = "a string"; //refers to a string
alert(typeof(variable));

</script>

</body>
</html>
Image from book

JavaScript does not consider this to be an error, because this is how it was designed to work (but this particular code snippet may not be a wise way to write your code, of course). Any variable can point to any object at any time. At first glance, this lack of type checking may not seem important, but it is a key element to the power that JavaScript can bring to your code. It is also central to some of the complications you will encounter when programming with JavaScript.

The Microsoft AJAX Library takes advantage of the extensible nature of JavaScript to extend its functionality to provide a more familiar object-oriented approach to development, even when using a typeless language, by layering some additional standard type treatment onto the language. Microsoft couldn’t modify the underlying typeless nature of the language, but they were able to provide a better way of using it that will allow you to represent types in some ways that help to mitigate the underlying limitations. In addition to the basic building block types, two other types are central to programming in JavaScript. Anything that is not a number, Boolean, or string is either a function or an object. A function in JavaScript is a first-class type. It can be passed as an argument to another object. A function can receive arbitrary arguments to act on. It can return primitive types, or it can even return a new function. Listing 3-5 (CreateFunction.aspx) presents an example that is admittedly not very useful in the real world but will illustrate this point. The CreateFunction function actually creates and returns one of two new functions that are stored and then invoked by calling CreateFunction. By calling CreateFunction you produce a function to either double or triple numbers. In reality, you would just use the built-in math libraries for this sort of thing.

Listing 3-5
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Code Test Page</title>
</head>
<body>

<script type="text/javascript">
function CreateFunction(s) {
    if (s ===2) {
        return function (s) { return s*2; }
    }
    else if( s=== 3) {
        return function(s) { return s*3; }
    }
}

var DoubleIt = CreateFunction(2);
var TripleIt = CreateFunction(3);

alert(DoubleIt(2));
alert(TripleIt(4));

alert(typeof(CreateFunction));
alert(typeof(DoubleIt));

</script>
</body>
</html>
Image from book


Language Anomalies

Despite using a familiar syntax with keywords you probably recognize, there are a couple of things about JavaScript that can catch you by surprise. How objects are compared for equality is probably different than you expect. And the JavaScript language has the notion of objects being null or undefined. If you don’t recognize these unique characteristics of JavaScript, you can get yourself into trouble and spend a lot of time debugging to find the source of the problem.

What Is Equality?

If you are just getting started with JavaScript and coming from a compiled language like C# or VB.NET, be prepared for a whole new kind of debugging challenge! I first came to JavaScript from a “vanilla” C programming background and was surprised by how many of my programming mistakes the compiler was catching. Not only is JavaScript typeless; it also doesn’t use a compiler! The only way to detect syntax errors, even simple ones such as improper case, is to run the code. And you must exercise every possible code path just to do what a compiler would do for you. This is one example of the huge benefit you can get from using the Microsoft ASP.NET AJAX client script library: There’s a large amount of fully debugged code ready for you to use in your own applications!

As mentioned previously, JavaScript shares its treatment of Boolean values with the C language. JavaScript and C need a double equals sign for a logical test, and they use a single equals sign for an assignment. This can become painfully apparent when you find a missing equals sign in a comparison test. JavaScript programmers quickly find that assignment is not equality, except in the eyes of the script! In Listing 3-6 (from AssignmentIsTrue.aspx), I attempt to test the value of the year variable but mistakenly include only one equals sign instead of two. This means that I am actually assigning a new value to the variable, and the value of this assignment is nonzero, so when treated as a Boolean, the assignment evaluates to true.

Listing 3-6
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Assignment Is True</title>
<script type="text/javascript">
var year = 1971;

if(year = 1968) {
  alert("this is true");
}
else {
  alert("this is false");
}
</script>

</head>
<body>
    <form id="form1" runat="server">
    <div>

    </div>
    </form>
</body>
</html>
Image from book

You might be telling yourself that this is a silly mistake that you aren’t likely to make. If you’re a C# programmer, you may be lulled into a false sense of security because the compiler catches these errors for you, but these kinds of errors are still common in the real world. In addition, there is more to this com-parison-testing subject in JavaScript. Even if you never accidentally use a single equals character instead of two, you might still not be safe from problems with equality checks. You may intend to test for equality, but JavaScript’s type coercion can still prove to be a problem. Consider the code in Listing 3-7 (Equality.aspx). The album variable is defined and then checked with the equality operator against null. It is true. The album variable has not been assigned yet and when evaluated against null is seen as equivalent. The next check shows that the variable is not true or false when checked explicitly. After that, the album variable is treated as a Boolean, and by definition a Boolean can’t be both not true and not false. It’s got to be one or the other, so it is coerced into one automatically, and JavaScript will treat it as false (the “!” not operator forces it to be treated as a Boolean even though we already determined that it’s not Boolean). Is your head starting to spin? Other types will be coerced automatically for comparison as well. The next equality check shows the string 5 being compared against the number 5 and evaluating to true. The number is coerced to a string for comparison purposes, similar to .ToString() in .NET. Boolean true and false values are treated as one and zero, respectively. Zero is false and nonzero is true. But the strings are not converted to equivalent Booleans, which hardly seems to make sense anymore. The last comparison shows that JavaScript also has the concept of variables being undefined. Because nothing has been assigned to album, its type is undefined.

Listing 3-7
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Equality</title>
<script type="text/javascript">
var album;

if(album == null){
 alert("album is null");
}

if ((album != true) && (album != false)) {
 alert("album is not true or false");
}

if(!album) {
 alert("but !album is true");
}

if (5 == "5") {
 alert("number 5 equals string 5");
}

if ((0 == false) && (1 == true) && (!0) && (1)) {
 alert("0 is false and 1 is true");
}

if (('true'
 alert("but 1 is not
}

if ('undefined'
 alert("the album type is undefined");
}
</script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    </div>
    </form>
</body>
</html>
Image from book

Null, Undefined, or Something Else

The subtle difference between something being undefined and null can easily turn into a problem in your code. One approach is to take advantage of JavaScript’s type coercion with a double negative. Instead of checking explicitly for undefined and null, you can apply the inverse operator twice: if (!!something). This approach can be extended to equality comparisons to avoid some of the confusion in the previous example. The string 5 is not really equivalent to the number 5 because the types are different, and in many cases you don’t want them to be treated as equal.

JavaScript originally had the equality and inequality operators that were in earlier examples. Later on, the strict equality and strict inequality operators were introduced. Instead of using two equal signs, you use three. This syntax results in a comparison performed without coercing types, so the types must be equal, and the values must be equal. In Listing 3-8 (from Inequality.aspx), I have duplicated the previous code listing but have changed to using the strict equality and inequality operators. The results are more intuitive and generally different from the previous set of comparisons. Notice that the undefined album variable is not strictly equivalent to null. It is actually 'undefined’, which means it doesn’t have a current type. And while it is undefined, it is neither true nor false as it was before with the standard equality operators. The number 5 is no longer considered equivalent to the string. And 0 and 1 are not really the same as true and false. Null is a value, not a type, and if you assign a null to a variable, it will take on the object type, which is not the same as having a type of undefined.

Listing 3-8
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Inequality</title>
<script type="text/javascript ">
var album;

if(album !== null){
    alert ("album  is not null");
}

if ('undefined' ===
    alert("the album type is undefined");
}

if ((album !== true) && (album !== false)) {
    alert("album is not true or false");
}

if (5 !== "5") {
    alert("the number 5 is not equal to the string");
}

if ((0 !== false) && (1 !== true)) {
    alert("false is not 0 and true is not 1");
}
</script>
</head>

<body>
    <form id="form1" runat="server">
    <div>

    </div>
    </form>
</body>
</html>
Image from book

I recommend using the strict equality operators as your default for performing comparisons. You should only use the original equality comparisons when you specifically want the types to be coerced as part of the comparison. This will save you hours of frustration in the long run.


Scoping Variables

Not only can variables in JavaScript be used to refer to any type of object at any time, they can actually be used without being declared at all! This can be a source of problems that are hard to identify because it will not be considered an error if you mistype a variable name. Rather, JavaScript will just assume you’re introducing a new variable. A variable’s scope is the context of code where that variable can be accessed. Whenever a variable is defined, it is assigned a scope for access based on where it is in the code. It is will be available to all inner scopes; loops, function calls, and further nested control structures. Basically, it will be available from the point it’s first defined until the scope in which it was defined exits. A variable is never available at a higher scope. If you declare a variable for use within function X, for example, that variable and its value are not available to the caller but are available to any functions that X calls. In fact, the variable is available to any functions called by functions that X calls. This means there’s no concept of local variables that aren’t visible outside your function (only those you call, and the functions they call can see them).

JavaScript will allow you to use a variable without declaring it (where it will be implicitly declared), but it changes the scope for that variable from what you would typically expect. An undeclared variable is automatically assigned global scope. In many cases, this is not a problem. A variable used within a function may not be accessed from a containing scope, so no other code is affected. Some variable names may be reused, however. And without requiring explicit declaration, the outside scope can be affected unintentionally. This can result in problems that are hard to debug, since they depend on the scope in which variables are used in addition to where the problem occurs.

In Listing 3-9 (from scoping.htm), two values are assigned to variables, one of which is defined and one of which is not. The same thing is done inside a nested function call. The function is able to modify both the declared and undeclared variables from the parent scope. When the function returns, the caller is now able to use the variable that was used without declaration inside the function. The other variable is scoped to the function and is no longer available after the function returns.

Listing 3-9
Image from book

<script type="text/javascript">

//use of a global variable without declaration
someUndeclaredVariable = "some undeclared variable";

//global variable declaration
var someDeclaredVariable = "some declared variable";

function someFunction() {
    //variable declared local to the function
    var nestedDeclared = "a nested declared variable";

    //undeclared variable used in function becomes global in scope
    nestedUndeclared = "a nested undeclared variable";

    //update the global variables
    someUndeclaredVariable = "modified undeclared variable";
    someDeclaredVariable = "modified declared variable";
}

someFunction();

//check the existence and values of the variables
if(!!someUndeclaredVariable) {
    document.write("someUndeclaredVariable = " + someUndeclaredVariable + "<br
/>");
}

if(!!someDeclaredVariable) {
    document.write("someDeclaredVariable = " + someDeclaredVariable + "<br />");
}

if(!!nestedUndeclared) {
    document.write("nestedUndeclared = " + nestedUndeclared + "<br />");
}

if(!!nestedDeclared) {
    document.write("nestedDeclared =" + nestedDeclared + "<br />");
}

</script>
Image from book

Figure 3-1 shows the output of running the page from within both Internet Explorer and Firefox. Although there are attempts to access and write each of the four variables, only the two used in the global scope and the one used in a nested scope without declaration are available. The undeclared variables are automatically elevated to a global scope. The declared variable is limited to the scope in which it is defined. In a simple example like this, it appears straightforward, but in real-world, complex development tasks, the scoping of undefined variables can easily lead to problems that are hard to isolate and fix.

Image from book
Figure 3-1

In Figure 3-1, notice the small yellow exclamation point icon in the lower-left corner of Internet Explorer. When it is double-clicked, a dialog will appear that explains that there is a script error on the page. In fact, you can instruct Internet Explorer to treat this as an error and instead of getting an exclamation point in the corner, you will get a dialog box alerting you to the use of the undeclared variable. The details view says that script is attempting to use an undeclared variable. In Firefox, the Tools menu has an option for launching a JavaScript console for working with script on the page. The console will show additional warnings when running in strict mode. To access the configuration settings, enter about:config in the address bar. A long list of available configuration settings will appear. Any setting that has been changed from the default value will be in bold text, while the rest are in regular text. Scroll down until you see javascript.options.script and double-click the line. That will toggle the value to true from its default of false. Use of undeclared variables will then be flagged in the JavaScript console as a warning. The browser window itself won’t show an error, but the JavaScript console will show the warning when it is made visible by selecting it in the Tools menu. Internet Explorer and Firefox both provide ways to find the use of undeclared variables because of the problems that arise when coding that way. Always declare variables to limit their scope and avoid modifying data unintentionally. Figure 3-2 shows the configuration screen for Mozilla Firefox and the JavaScript console, with a warning visible for using an undeclared variable.

Image from book
Figure 3-2

Sometimes you want to test to see if a variable is in scope without causing an error. This can be done by testing for it as a member of the window class. If an object exists, it will be coerced to true in a Boolean test; if false, it doesn’t exist:

if(window.nestedDeclared) {
    document.write("nestedDeclared =" + nestedDeclared + "<br />");
} else {
  document.write("nestedDeclared is not in scope");
}


Using Prototypes

The function type in JavaScript allows you to define custom types. JavaScript doesn’t support static type checking, so you can be creative in creating and using custom types on the fly. The object-oriented programming paradigm has made programming at a higher level the standard for many developers and is more appropriate than procedural programming for most tasks. JavaScript does not support inheritance as other object-oriented languages do, but functions can be used to model data types. As a dynamic language, JavaScript opens the door for some interesting type definitions that can make the code look more familiar to C# and VB.NET developers.

Listing 3-10 (from album.cs) includes a simple definition of an album object in C#. There are string variables for storing the title and artist. There is a constructor that takes the artist and title and stores them in the local variables.

Listing 3-10
Image from book

using System;

public class Album
{
  private string _title = String.Empty;
  private string _artist = String.Empty;

  public Album(string title, string artist)
  {
    this._title = title;
    this._artist = artist;
  }
}
Image from book

You can model the same object in JavaScript using a function. Listing 3-11 (from album.htm) has a JavaScript function with two arguments: title and artist. This is just like the constructor in C#. It takes the two arguments and assigns them to the local variables declared inside the function.

Listing 3-11
Image from book

<script type="text/javascript">
function Album(title, artist) {
 this._title = title;
 this._artist = artist;
}

var album1 = new Album("Lawn Boy", "Phish");
var album2 = new Album("A Picture of Nectar", "Phish");
</script>
Image from book

The album type can be extended to include a property that provides a string representation with both the title and artist. In C#, this would mean adding a new property to the class and recompiling it. In JavaScript, a type can be modified while the script is still running. The prototype property of an object represents the type itself (which shouldn’t be confused with a C language prototype, which is only used to give a function a signature). When using the prototype to extend a type, the change applies to all instances of the object. Listing 3-12 (from album.htm) adds the Listing property to the album type using the prototype.

Listing 3-12
Image from book
<script type="text/javascript">
function Album(title, artist) {
 this._title = title;
 this._artist = artist;
}

var album1 = new Album("Lawn Boy", "Phish");
var album2 = new Album("A Picture of Nectar", "Phish");

function listing() {
 return this._artist + ": " + this._title;
}

Album.prototype.Listing = listing;

alert(album1.Listing());
alert(album2.Listing());
</script>
Image from book

The prototype object represents the object type itself; think of it as a type definition. Any instance of the object inherits all of its prototype information. All of the objects of that type that have already been created get the updated abilities of the base type immediately. Of course, a new function can be added to the object definition so that all instances of the object created in the future will receive the functionality without having to modify the prototype directly. The disadvantage is that each instance gets a copy that way. Using the prototype of an object to extend its functionality is a natural way to model static methods and properties in C# and C++. Modifying the prototype in JavaScript is like modifying the basetype in a .NET type without requiring derived types to be aware of the change.

The prototype object of JavaScript even allows built-in types (such as String) to be modified. When you add functionality to the intrinsic objects provided by the scripting engine, you begin to appreciate the power and flexibility of a dynamic language. You aren’t forced to subclass the type and switch all of your code to use some new derived type to add something that you need. In .NET code, you could do this by re-compiling, when you have access to the original class sources, but in JavaScript you can modify a type without access to the sources. Listing 3-13 (from string.htm) adds the StrangeCaseIt method to the built-in JavaScript String type. Notice that the string is declared and assigned before the new function is created. The StrangeCaseIt function is then added to the String using its prototype. Now all strings, including the one that already exists in the code, have access to this new functionality but haven’t had to pay any overhead associated with carrying the information about the new method along.

Listing 3-13
Image from book

<script type="text/javascript">
var sample = "SomeThing";

function StrangeCaseIt() {
    var returnVal = "";
    for(var i = 0; i < this.length; i++) {
        if(this.substr(i, 1).toUpperCase() == this.substr(i, 1)) {
         returnVal += this.substr(i, 1).toLowerCase();
        }
        else {
            returnVal += this.substr(i, 1).toUpperCase();
        }
   }
   return returnVal;
}

String.prototype.StrangeCaseIt = StrangeCaseIt;

var result = sample.StrangeCaseIt();

document.write(sample + "<br />");
document.write(result + "<br />");

</script>
Image from book

The Microsoft AJAX Library makes heavy use of the prototype object to extend functionality and provide a more natural programming environment for the .NET developer. Chapter 7 goes into more detail about this extended functionality and how a familiar object-oriented approach is made available with ASP.NET AJAX. For now, just note that this type system functionality is provided by leveraging the prototype feature of JavaScript.

When using the prototype object, the use of the this reference can be confusing. “this” refers to the object currently in scope at the time the code runs. Remember, the prototype applies to the type itself and all instances of it. It is a specific reference meant to eliminate global members from being included in an object you are extending. When creating object extensions using the prototype, omitting the this reference to use object variables is interpreted as an error. You must specify that the reference is for another variable in the object scope. Think of the prototype extensions as operating in isolation from the rest of the code. You don’t want to be affected by the scope in which the object runs, so you have to be specific about every reference you make. This has to do with the fact that JavaScript uses lexical scoping, not dynamic scoping. Instead of allowing you to reference anything in scope for a prototype extension when the function is called, the object must exist when the function is defined. Always use the this operator to refer to object variables because anything that is not part of the object definition could be different when the code executes next and could lead to problems.


Close on Closures

When discussing the prototype object, it’s necessary to consider JavaScript closures. The prototype is used to extend the definition of an object, regardless of what other scope is in effect. The prototype is available as a characteristic of the type, regardless of whether you even have an instance of the object. The prototype is independent from the state during which it was created. Closures, on the other hand, are used to leverage the scope in play at the time the closure is created. A closure refers to how an inner function can be defined that can take advantage of the state of the outer function’s context and be made visible externally so it can be executed after the outer function has returned. The behavior of the closure can depend on the state and scope that existed when the closure was created. Scoping behavior is the same in JavaScript as it is in C# and VB.NET, with one notable exception as was shown in Figure 3-1: Using a variable without declaring it gives it a global scope.

If you search for information on closures, you are bound to find some hard-to-follow, overly complex scientific explanations. Concrete specific examples are generally better at explaining closures than formal definitions. The prototype can reference properties and functions using the this operator, but the lexical scoping means that any outside variables that happen to exist do not come into play. It is typically an error to try and use transient state information in a prototype extension. The various instances of the object type being extended shouldn’t leverage any other scope that happens to be in existence, unless it is deliberate access to global information. A closure function captures the state of its scope at the time it is created.

As I’ve stated, all of this talk about scoping is much easier to see in an example. Listing 3-14 (from clo- sure.htm) contains a function that creates another function. The function created just squares the variable according to the scope that was in effect when the closure was created. The code loops from 0 to 10, calling the function creator and storing the results. Then another loop goes through the stored functions and invokes each one.

Listing 3-14
Image from book

<script type="text/javascript">
function CreateFunction(num) {
    var NewFunction = function() {
        document.write(num + " squared = " + num*num + "<br />");
    }
    return NewFunction;
}

var functions = new Array();

for(var i = 0; i < 10; i++) {
    functions[i] = CreateFunction(i);
}

for(var j = 0; j < 10; j++) {
    functions[j]();
}
</script>
Image from book

When the functions are created, they carry with them the scope that existed at that time. It doesn’t matter that the variable used when creating the function has subsequently changed or even gone out of scope. The function reflects the closure of the scope in effect at the time it was created. The num variable no longer exists once the function-creating method returns, but the state of the transient variable is captured and used when the new function is called. Figure 3-3 shows the output of calling the functions to output their values.

Image from book
Figure 3-3

In the previous example, the scope of the variable being used was limited to the function that was creating new functions. The variable is declared as a function argument and is no longer available after the function returns. However, the closure that is created captures the state of that variable in that scope, and the function created is able to use it later when it is invoked. In Listing 3-15 (from ClosureScope.htm), I modify the example slightly to show how the scope is captured in a closure. A variable in the global scope is referenced from within the function being created. The variable is declared before the loop that is creating functions, and the variable is incremented during each pass. When the created functions are called, the scope that existed at the time of creation is used, not the value in play at that time. Although the value for the variable was different while each closure was created, just as in the previous example, the scope was different and thus the function is different.

Listing 3-15
Image from book
<script type="text/javascript">
function CreateFunction(num) {
 var NewFunction = function() {
  document.write( num + " squared = " + num*num +
                  " and globalNum= " + globalNum + "<br />");
 }
 return NewFunction;
}

var globalNum = 0;
var functions = new Array();

for(var i = 0; i < 10; i++) {
 globalNum = globalNum + 2;
 functions[i] = CreateFunction(i);
}

for(var j = 0; j < 10; j++) {
 functions[j]();
}
</script>
Image from book

Every function that was created shows the current value of the global variable based on the captured scope. The variable was global when the closure was created, and the value at the time is immaterial. The scope is captured, and the value of the variable when the closure function is actually called is used. Since it is in the global scope, the current value of the variable is still in effect when the individual functions stored in the collection of functions are called. They all show the same value for the global variable as it is now, despite that the value was different when each function was created, as you can see in Figure 3-4.

Image from book
Figure 3-4

Garbage Collection

JavaScript is a garbage-collected language. When an object goes out of scope and there are no longer references to it, the system can free it and reclaim the memory and resources. This is one of the language features of C# and VB.NET that make them easy to use. C++ programmers are used to painstakingly allocating and freeing memory and objects by hand. Managing your own memory can be tedious, and mistakes can be devastating. JavaScript tracks which variables refer to what, and when there are no lingering references to an object, it is freed.

DOM Elements

Ultimately, what makes JavaScript useful in the browser is that it is able to access the DOM (Document Object Model). You can attach JavaScript functions to browser events and dynamically modify the HTML elements, CSS, and behavior of the page. Thus, JavaScript can take static pages and make them rich and interactive. In Listing 3-2, code is attached to the onLoad event of the browser’s window element.

As browsers evolved, they developed different Document Object Models. The World Wide Web consortium established a standard meant to provide isolation from the differences. In reality, there are still some challenges in writing code that will work on all of the major browsers, but the Microsoft AJAX Library provides some abstractions that make it easier to write code compatible with many popular browsers.

Avoiding Memory Leaks

Closures, garbage collection, and access to the DOM come together in a way that can manifest itself as a memory leak in the browser. It is easy to end up with circular references that involve a DOM element. The garbage collector in Internet Explorer sees this as a persistent reference and fails to recognize that the object can be freed. Even when a new page is loaded, the resource is still held, although the element does not exist anymore. Over time, the amount of memory consumed by the browser will creep up until the process is restarted.

A common way to create circular references is to attach a JavaScript function to the event of an HTML element. If the event handler code then references a property of the element, you have a circular reference. The Microsoft AJAX Library provides constructs for binding events that also clean up the circular references.

The process many developers use to avoid memory leaks is to attach to the unload event of the window. This event will fire when the user is through with the page and moving on. In the event handler, the user will loop through DOM and set the event handlers to null to break the circular references. Listing 3-16 (from circular.htm) is an example of attaching an event handler to a DOM element that creates a circular reference by accessing the source element.

Listing 3-16
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Circular Reference</title>
<script type="text/javascript">
function eventHandler() {
    var tb = document.getElementById("tb");
    if(!!tb) {
        tb.value = "something else";
    }
}
</script>
</head>
<body>
<form action="HTMLPage.htm">
<input type="text" id="tb" onclick="eventHandler()" />
</form>
</body>
</html>
Image from book


Evaluating eval

In a compiled language like C# and VB.NET, it is difficult to create and use code dynamically. The .NET Framework provides the CodeDOM (code document object model) classes for defining functionality and then producing the equivalent code, but this is not typically employed during code execution, because of its difficulty. JavaScript is dynamic in that it allows types to be created and modified on the fly. Furthermore, it natively supports the ability in the eval function to have any arbitrary script executed. It functions just as if it were a predefined piece of code. This means that you can easily write code that uses runtime values during code execution to create new code that is then executed. Essentially, you can create and execute code on the fly based on other input and variables.

Variable assignments can be based on any runtime condition and used elsewhere in JavaScript code. In Listing 3-17 (eval.htm), I assign some names to variables. I then create a string which is a piece of code that aggregates the values into a single variable and then displays that variable. Note that the values are pulled together into a piece of code that will use them all. This is distinctly different from having a piece of code that combines strings. The variables happen to be strings, but the eval statement is simply executing a piece of code passed to it as a string.

Listing 3-17
Image from book

<script type="text/javascript">
var aName = "Heather";
var anotherName = "Josh";
var yetAnotherName = "Kelley";
var stopAlready = "and Adam";

var someCode = "var names = '" + aName + ", " + anotherName + ", " + yetAnotherName
+ ", " + stopAlready + "'";
var moreCode = "eval(someCode); alert(names);";

function showNames() {
    eval(moreCode);
}

showNames ();
</script>
Image from book

Of course, just gathering some strings together and displaying them can be accomplished without the use of the eval function. But if you find yourself iterating over a collection in code where you are writing repetitive code in a pattern, consider whether eval might make the code easier to read and maintain. The eval function is a powerful way to execute any arbitrary JavaScript code on the fly. In this example, the call to showNames results in performing an eval on the moreCode variable, which in turn evals the variables and displays the values. It’s a convoluted way to get the job done, but the point is that you can compose code based on dynamic values.

eval does not come without tradeoffs. Code executed with eval is generally slower than code run directly. Having code that runs other code is a level of indirection that comes at a price. Numerous articles and blog posts advocate never using eval at all. Not only is performance a concern, but security comes into play as well. It is very tempting to use eval to operate on user input, but user input cannot be trusted. In the confines of the browser sandbox, much of what a user might try to do wouldn’t reveal anything interesting, but treating user input as trusted code and executing it can open all sorts of unforeseen security concerns. Generally, you should never use eval on user input.

One common use of eval is to access fields of an object. Instead of calling eval("something." + someField), you can usually switch to using something["someField"] instead. This is generally more intuitive for others to read, easier to maintain, and faster in the browser.


Handling Exceptions

JavaScript supports a familiar type of exception handling for C# developers. Code can throw an exception. If located inside a try block, a corresponding catch block can handle the exception. After that, a finally block will execute to take care of any necessary cleanup or global recovery approach. The try-catch-finally pattern is important in being able to write robust code that can recover from error conditions.

The Java runtime environment (not JavaScript) requires that code declare up front any type of exception that it may want to catch. When it is compiled, the type of exceptions that may be thrown are checked. You must explicitly catch all of the possible exception types. The .NET Framework does not require declaring what type of exception a method may throw. You can catch the base-exception type and then deal with more specific exception types as you see fit. You can even define custom exception types derived from existing types for greater specialization. In JavaScript, there are no static checks on types of exceptions. You simply catch the exception, which has a base type of object, and react as you see fit. It is not uncommon to have exception-handling code that looks at the details of the exception thrown to determine what the course of action should be. In many situations, throwing a basic Exception using a string constructor is adequate to signal what recovery action is necessary. This choice is entirely a runtime determination. Listing 3-18 (Exceptions.aspx) shows a set of try-catch-finally blocks. The runtime overhead and complexity associated with exception-handling semantics in other languages is not the same in JavaScript. If your preference is to use exceptions as part of flow control, you need not worry about a significant impact on performance.

Listing 3-18
Image from book

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Exceptions</title>
<script type="text/javascript">
<script type="text/javascript ">
try {
    var now = new Date().getTime();
    if ((now % 2) === 0) {
        throw "even ticks";
    }
    else {
        throw "odd ticks";
    }
}
catch (ex) {
    if(ex === "even ticks") {
        document.write("an even exception occurred <br />");
    }
    else if (ex === "odd ticks") {
        document.write("an odd exception occured <br />");
    }
    else {
        alert(ex);
        document.write("a mathematical anomaly occured");
    }
}
finally {
    document.write("all done");
}
</script>

</script>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    </div>
    </form>
</body>
</html>
Image from book


Getting Parts of a String

Switching back and forth between languages can have its pitfalls. There may be something slightly different between the two that continues to trip you up. Accessing parts of a string is a common programming task and one that may trip you up as you go back and forth from JavaScript to C# or VB.NET.

The substr function for extracting part of a string is used in C and C++, while the substring method is supported in C# and VB.NET. They are both available in JavaScript. They all operate on 0-based strings. If you are also using Visual Basic, which is 1-based, this may be an initial stumbling block. Both substr and substring have versions that accept a single argument and return the remainder of the string starting at that index. The two argument versions behave slightly differently. The substr function takes a start index, and the number of characters to extract. The substring function takes a start index and an end index. In isolation, this seems straightforward, but you will undoubtedly be coding in something other than JavaScript part of the time and may find yourself scratching your head trying to remember which language supports which version. JavaScript has them both. The result of running Listing 3-19 (from substrings.htm), where both versions are called with identical arguments, is shown in Figure 3-5.

Image from book
Figure 3-5
Listing 3-19
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>substrings</title>
    <script type="text/javascript">
var aString = "0123456789";

document.write(aString.substr(2) + "<br />");
document.write(aString.substring(2) + "<br />");
document.write(aString.substr(2, 5) + "<br />");
document.write(aString.substring(2, 5) + "<br />");
</script>
</head>
<body>

</body>
</html>
Image from book


Better JavaScript

As you leverage JavaScript more in creating richer web applications, there are a few practices to be aware of that may help you produce more robust and faster pages. For all of the advantages JavaScript brings, it also has some drawbacks that become apparent the more predominant a part of the page it becomes. The amount of script in a page can easily surpass the amount of HTML. Performance can start to lag.

Reducing Script

With bigger quantities of script being used in the page, the size of the script itself can become an issue. IIS supports compressing responses, but it is not turned on by default. Text compresses very well, which greatly reduces the number of bytes sent to the client. To enable compression in IIS, access the Internet Services Manager plug-in for the Microsoft Management Console. You can do this by entering inetmgr in the run dialog in the start menu. Right-click the Web Sites folder and select Properties. Options for enabling static and dynamic content are on the Services tab. There is CPU cost associated with compression, so you may want to enable it only for static content if the dynamic content is not cacheable (since the static content is always cacheable, it can be compressed once and not repeatedly).

Another technique for reducing the size of script is variable substitution. You will see this referred to as script compression. The approach is to replace variable names with short versions. When writing code, it is nice to have meaningful names, but because the script engine doesn’t care about this, smaller names also work fine. Tools are available that will take script and do this replacement for you. However, it can make debugging difficult, as the variable names and even function names can be replaced with short, meaningless versions. It may seem like reducing the size of the script doesn’t buy you much, and for a single page load, the difference will be small. But when you look at the effect it can have on the server, there is a big benefit. Smaller scripts can be returned faster. The connections and threads used to service the request are freed up sooner, so a single server can scale to accommodate more simultaneous users.

Using Cache Variables

In a compiled language, the code generator can do some tricks to make the executing code faster. A variable referenced repeatedly in a function may have its pointer stored in a register for easy and fast access throughout the function. In JavaScript, variable resolution is late bound. Each time a variable is used, the scope chain is searched to find the object. This can be time consuming. Rather than building up strings while storing them in a DOM element, it’s better to build them up in a local variable and then do a single assignment to the DOM element. When looping through arrays, you can actually improve performance by initializing a variable with the length of the array and checking against that instead of accessing the length property each time.

for(var i=0, var l = someArray.length; i < l; i++) is faster than

for(var i=0; i < someArray.length; i++).

The kind of speedup you might see from this sort of optimization is not nearly as dramatic as what you can get with caching of function pointers. JavaScript function lookups are expensive, and scope resolution and lookups to access functions on DOM elements are even more expensive. One way to think of it is that reducing the number of dots in your code improves the performance. Listing 3-20 has two versions of the same method, inspired by the blog of the Internet Explorer team. While there is nothing technically wrong with the first version, the second will be much faster for any reasonably large collection.

Listing 3-20
Image from book

<script type="text/javascript">
function UpdateValues() {
    var elt = document.getElementById("something");
    for(var i = 0; i < myArray.length; i++) {
        elt.appendChild(myArray[i]);
    }
}

function UpdateValuesCaching() {
    var eltFunc = document.getElementById("something").appendChild;
    for(var i = 0, var l = myArray.length; i < l; i++) {
        eltFunc(myArray[i]);
    }
}
</script>
Image from book

The second version caches a reference to the appendChild function and then invokes it directly. This saves the overhead of repeatedly resolving the function name to the actual function call. When multiple scopes and namespaces are involved, the performance advantage is increased.


Summary

This chapter provided a brief introduction to JavaScript. You saw how script is included in a page and explored techniques for minimizing the overhead associated with loading script. JavaScript is a dynamic language that allows types to be modified on the fly using the prototype. You can also create closures with functions that capture a given scope for even greater flexibility. Not only does JavaScript have support for the fundamental types of string, number, and Boolean, but it also treats functions as first-class types as well as objects. The late binding nature of JavaScript means that you can improve performance by caching aggressively. And you’ve also seen that JavaScript can be confusing with support for null as well as undefined types. The equality operators and strict equality operators can complicate things further, so being explicit about what you are after will eliminate frustration in the long run.


Chapter 4: Understanding the ASP.NET AJAX Client Library

Many of the application improvements you get in using AJAX are centered on doing more in JavaScript in the browser. This includes asynchronous communications with the server, preloading images and data, more manipulation of the browser DOM, and more. What these improvements all have in common is an increased use of JavaScript to accomplish these tasks. ASP.NET AJAX provides some key features that make this easier. In this chapter you will look at the type system provided by the Microsoft AJAX Library that makes it easier to employ familiar OOP (object-ori-ented programming) patterns in your JavaScript development. You will also see a set of JavaScript classes that provide core functionality similar to the base class libraries of the .NET Framework. The AJAX Library brings concepts from the .NET Framework to JavaScript running in the browser, making the server and client coding models a bit more consistent. However, since JavaScript is inherently quite different from C# or VB.NET, and since you cannot leverage the .NET Framework on the user’s computer, you shouldn’t expect a high degree of compatibility and consistency here. The AJAX Library has also added a client-side page lifecycle, similar in concept to the ASP.NET server-side page lifecycle. This addition makes it easy to participate in the processing of the page, work with partial page rendering, and provide event handlers for user actions.

I should point out here that with ASP.NET AJAX, you will get many of the application advantages without writing a lot of JavaScript yourself. In Chapter 2, for example, you saw how to use server controls that automatically generate JavaScript for you to enable partial page rendering. In Chapter 8 you will see a set of controls from the AJAX Control Toolkit that provide some interesting UI features that leverage the Microsoft AJAX Library and do not require you to write a lot of JavaScript. Microsoft has done a great deal of work for you, which you can easily leverage in your applications. Once you want to write your own controls, add custom event handlers, or start doing more in the browser, however, you will likely need to write custom JavaScript code. The ASP.NET AJAX Client Library and some recommended coding patterns can make you more productive in that work.

The Browser Page Lifecycle

One of the first things to understand about the ASP.NET AJAX Library is that it establishes a page lifecycle for JavaScript code running in the browser. It is not as complex as the server page lifecycle but it is central to interacting with some of the more complex features. This lifecycle gives you some easy “hooks” that you need to tap into in order to do the processing you want to do at the right time. Although the JavaScript event mechanism is simpler than that provided by the .NET Framework, it is sufficient for your needs. Listing 4-1 (from BasicAJAX.aspx) shows a very basic page built with ASP.NET AJAX. It just pops up a box with the word hello, but it shows a basic starting point. To use the Microsoft AJAX Library and initiate the client-side lifecycle in the browser, there is a ScriptManager control on the page. This takes care of rendering the references to the scripts that make up the library, which the browser will have to request when the page loads.

Listing 4-1
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
<script type="text/javascript">
function pageLoad(sender, args) {
   alert("hello");
}

function pageUnload(sender, args) {
   alert("goodbye");
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ID="sm" />
<div>
</div>
</form>
</body>
</html>
Image from book

The pageLoad and pageUnload functions are the primary points where you will interact with the browser page lifecycle. The pageLoad function is automatically called after the page is initially retrieved from the web server and some script processing has occurred. The pageUnload function is then called whenever a subsequent request is initiated (for postback, partial page rendering, or even when navigating to a different application). When the page is loaded again, even during partial rendering, the pageLoad function will again be triggered. Chapter 11 discusses building custom controls and also shows additional details of the page lifecycle.

For most of the rest of the code samples in this chapter, I will omit the HTML markup and inclusion of the ScriptManager. The examples will focus on the script you will write and how to use the type system and base class library features of the AJAX Library.


Using the Type System

JavaScript has a familiar syntax if you are used to C#, Java, or C++. But as you saw in Chapter 3, JavaScript does not have built-in support for most OOP (object-oriented programming) concepts. This is not a problem by itself, but as you switch back and forth between C# or VB.NET and JavaScript, you may find that you miss the familiar OOP constructs. There is no support for inheritance of types and no standard way of declaring interface definitions or enumerations. What’s a developer to do? In the past, it meant that you adopted your own conventions in writing JavaScript. This becomes problematic as more people with unique approaches are added to a project, because there can be inconsistencies that make maintenance and new development more difficult. ASP.NET AJAX proves the ability to leverage OOP constructs when writing JavaScript. You will see a lot of small code examples that demonstrate a set of recommended standard patterns and show how the Microsoft AJAX Library functions are used.

The AJAX Library brings classic OOP concepts to JavaScript. It adds namespace support for grouping functionality. It also provides helpful debugging facilities, a schema for providing type information, and a means for localizing string and image resources.

Part of the ASP.NET team’s motivation for creating the AJAX Library and these patterns for development was to make JavaScript coding more like the C# coding that developers are used to. The team has tried to find the right balance between leveraging the features of a dynamic language and adopting a rigid approach more suitable to development with a statically typed language.

Declaring Namespaces

In the .NET Framework, like functionality is grouped into namespaces. Graphics classes are under the System.Graphics namespace, while ASP.NET Framework classes are under the System.Web names-pace. The use of namespaces is central to providing an organized and structured way for finding and accessing functionality. Namespaces are nothing more than prefixes used in front of class and type names, but through the use of standard conventions they are used to organize sets of similar functionality and are then typically housed in a single DLL. Your application declares what assemblies are referenced at compiletime, and this establishes what namespaces are available for use during development and which assemblies are required at runtime. The AJAX Library also supports the concept of names-paces. It uses namespaces for its own objects and provides you with the ability to do the same.

The AJAX Library synthesizes namespaces by creating objects with those names. Class definitions can be organized logically in just the same way that you would organize them using C# or VB.NET. You can group functionality naturally into separate namespaces and use them as independent resources. If you create separate files for different namespaces, you can even load them conditionally the same way the Common Language Runtime loads just the assemblies it needs for an application and does so on demand. This achieves a benefit similar to what happens in .NET Framework applications where the overhead associated with using some functionality in a namespace is not incurred unless the feature is used. It is a pay-for-play model. An application doesn’t need to send the script associated with a names-pace if the functionality isn’t used in that page.

Listing 4-2 (from Namespaces.aspx) demonstrates the basics of namespace support. The Wrox.ASPA- JAX.Samples namespace is created via Type.registerNamespace. Notice that you do not have to first create the Wrox and Wrox.ASPAJAX namespaces in order to register the Samples namespace under them. You can specify the whole namespace, and the AJAX Library will automatically create any levels of the hierarchy that do not exist already.

Listing 4-2
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>ASP.NET AJAX Namespaces</title>
<script type="text/javascript">
function pageLoad(sender, args) {
    Type.registerNamespace('Wrox.ASPAJAX.

    alert(Type.isNamespace(Wrox.ASPAJAX)); //displays
    alert(Type.isNamespace(Wrox.ASPAJAX.Samples.Album)); //displays

    var namespaces = Type.getRootNamespaces();
    for(var i = 0, length = namespaces.length; i < length; i++) {
        alert(namespaces[i].getName()); //displays
    }
}
</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager1" />
    <div>

    </div>
    </form>
</body>
</html>
Image from book

The call to Type.registerNamespace creates three different objects: Wrox, ASPAJAX, and Samples. The Wrox object contains the ASPAJAX object, which in turn contains the Samples object. The objects all carry some metadata so the type system can identify them as namespaces and use them to hold any other objects that are added to the namespace.

The Type.isNamespace function returns a Boolean. The code didn’t create an Album namespace, so for that check, it returns false. The set of global namespaces is retrieved by calling Type.getRootnamespaces. Looping through the returned array and calling getName on each reveals that, in addition to the new Wrox namespace, there is also a Sys namespace. It contains the AJAX Library functionality.

Although doing so is not technically required, I recommend using namespaces to organize your own code, even if just to avoid cluttering up the global namespace. Because JavaScript is an interpreter language, the operation of resolving names is expensive. Every time you call a function, the JavaScript engine has to figure out where the code lives. Resolving variables also involves searching the current scope and containing scopes until the reference is resolved. The more global objects you have, the more expensive it is for the script engine to access them. Namespace objects also allow navigating to classes in the hierarchy more readily than would happen in a flat global arrangement. Thus, namespaces offer a performance benefit as well as providing a programmer convenience for grouping functionality. Namespaces by themselves, however, are not much use until your create classes in them that will provide useful functionality.

Scope is not just important in resolving namespaces and variable namespaces. Listing 4-3 (from Scope.aspx) demonstrates scope in JavaScript. The code uses global variable x and passes it to a function called doSomething, where x is doubled. After the function returns, the value of x is the same as before the call. The change to x is scoped to the function. The call to the function is performed by passing a value, not a reference. The return value of the function is not assigned back to the global x variable.

Listing 4-3
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">=
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Trace Console</title>
    <script type="text/javascript">
var x = 38;
var y = 38;
function doSomething(x) {
    x = x + x;
    y = y + y;
    alert("x = " + x + " now and y = " + y);
    return x;
}
function pageLoad(sender, args) {
     alert("before calling doSomething x = " + x + " and y = " + y);
     doSomething(x);
     alert("x still equals " + x + " but y now equals " + y);
}
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager runat="server" ID="sm" />
    </div>
    </form>
</body>
</html>
Image from book

Creating Classes

JavaScript functions are used to represent class objects in the type system. The AJAX Library follows the pattern of declaring a function as the class constructor. JavaScript allows you to modify the prototype of the function directly, which is how the AJAX Library creates class members. The class must then be registered so that it can participate in the semantics of the type system. Listing 4-4 (Class.aspx) creates the Album type in the Wrox.ASPAJAX.Samples namespace. The constructor takes two parameters and assigns them to local variables. The object prototype is modified to declare two property accessors.

Listing 4-4
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>ASP.NET AJAX Class</title>
    <script type="text/javascript">
function pageLoad(sender, args) {
    Type.registerNamespace('Wrox.ASPAJAX.Samples.
    Wrox.ASPAJAX.Samples.Album = function(title, artist) {
        this._title = title;
        this._artist = artist;
    }

    Wrox.ASPAJAX.Samples.Album.prototype = {
        get_title: function () {
            return this._title;
        },
        get_artist: function() {
            return this._artist;
        }
    }

    Wrox.ASPAJAX.Samples.Album.registerClass('Wrox.ASPAJAX.Samples.

    var anAlbum = new Wrox.ASPAJAX.Samples.Album("Round Room", "Phish ");
    alert(anAlbum.get_title());
}
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager runat="server" ID="sm" />
    </div>
    </form>
</body>
</html>
Image from book
Tip

Notice that the local members are accessed with a prefix of this. The script engine can then scope the lookup to the type and avoid searching any containing scopes. If you do not use this to indicate that the reference is local to the type, you will end up creating objects in the global scope and see errors that can be confusing and time-consuming to track down.

The call to registerClass looks a little odd, as it is on the type being registered. The prototype of the base type in JavaScript has been modified to add type-system support. Once the type is registered, an instance of it can be created and its members called.

The registerClass function actually has three possible parameters: The first one is for the name of the type, the second is for the base type being extended, and the last is to specify any interfaces that the class implements. Instances of using these classes are provided in later examples in this chapter.

JavaScript treats parameters as optional. This can be convenient. Instead of needing to define a bunch of different methods with different names in order to accommodate different combinations of parameters, you can write just one that knows how to process all of the optional inputs. Because the language treats all parameters as optional, however, you need to explicitly check that the inputs are valid for what you are doing. The caller can invoke the function with whatever set of parameters it wants to pass.

Although JavaScript allows types to be dynamically modified, and ASP.NET AJAX uses this to add the type system functionality into existing objects, it is not the approach recommended for mainstream development. Instead of modifying a type directly, you can inherit from it and extend it in new members by overriding existing ones. In Listing 4-5 (Inheritance.aspx), the Album type is extended by the TributeAlbum class, and adds one more piece of information: the artist being honored.

Listing 4-5
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>ASP.NET AJAX Class</title>
    <script type="text/javascript">
function pageLoad(sender, args) {
    Type.registerNamespace('Wrox.ASPAJAX.Samples.
    Wrox.ASPAJAX.Samples.Album = function(title, artist) {
        this._title = title;
        this._artist = artist;
    }

    Wrox.ASPAJAX.Samples.Album.prototype = {
        get_title: function () {
            return this._title;
        },
        get_artist: function() {
            return this._artist;
        }
    }

    Wrox.ASPAJAX.Samples.Album.registerClass('Wrox.ASPAJAX.Samples.

    Wrox.ASPAJAX.Samples.TributeAlbum = function(title, artist, tributeArtist) {
        Wrox.ASPAJAX.Samples.TributeAlbum.initializeBase(this, [title, artist]);
        this._tributeArtist = tributeArtist;
    }

    Wrox.ASPAJAX.Samples.TributeAlbum.prototype = {
        get_tributeArtist: function() {
            return this._tributeArtist;
        },
        set_tributeArtist: function(tributeArtist) {
            this._tributeArtist = tributeArtist;
        }
    }

    Wrox.ASPAJAX.Samples.TributeAlbum.registerClass(
        'Wrox.ASPAJAX.Samples.TributeAlbum',
         Wrox.ASPAJAX.Samples.Album);

    var anotherAlbum = new Wrox.ASPAJAX.Samples.
Groove", "Various Artists", "Phish");
     alert(anotherAlbum.get_title ());
     alert(anotherAlbum.get_tributeArtist());
}
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager runat="server" ID="sm" />
    </div>
    </form>
</body>
</html>
Image from book

The constructor must explicitly call initializeBase and pass itself, using the this keyword, along with an array of the arguments to pass to the constructor of the base type. The AJAX Library allows you to employ object-oriented principles, but doing so requires that you follow some coding patterns like this. Without the call to initializeBase, when you try to call something on the base type, you will get an error. In Internet Explorer, the message reads: Object doesn't support this property or method. This is not the most helpful message! In Firefox, it fails silently, but if you have the JavaScript console open, an error message is displayed that more explicitly identifies the actual problem: anotherAlbum.get_title is not a function. Chapter 9 provides more details about debugging.

The call to initializeBase takes care of producing the final type with inheritance semantics in place. The base class constructor is called with the arguments provided.

The type system of the AJAX Library also provides some reflection functions that let you explore the relationship between objects. Listing 4-6 (from InheritsFrom.aspx) demonstrates calls to inheritsFrom and isInstanceOftype using the Album and TributeAlbum types. It is simply a copy of the Inheritance.aspx page with these lines added at the end of the pageLoad function. Again, you see that these methods are available automatically as part of using ASP.NET AJAX. The AJAX Library has modified the underlying JavaScript type so that any class you register can use this functionality.

Listing 4-6
Image from book

if(Wrox.ASPAJAX.Samples.TributeAlbum.isInstanceOfType(anAlbum) == false) {
    alert("anAlbum is not a TributeAlbum");
}

if (Wrox.ASPAJAX.Samples.TributeAlbum.isInstanceOfType(anotherAlbum) == true) {
    alert("anotherAlbum is a TributeAlbum");
}

if (Wrox.ASPAJAX.Samples.TributeAlbum.inheritsFrom(Wrox.ASPAJAX.Samples.Album) ==
true ) {
    alert("TributeAlbum inherits from Album");
}

if (Wrox.ASPAJAX.Samples.Album.inheritsFrom(Wrox.ASPAJAX.Samples.TributeAlbum) ==
true) {
    alert("Album does not inherit from TributeAlbu7");
}
Image from book

At first glance, the type references in Listing 4-6 look long. JavaScript doesn’t have the equivalent of the using statement that makes namespaces available to code without being explicit. With a compiled language, the cost of the lookup can be paid when the binary is created and symbolic references are created. In an interpreted language like JavaScript, you can speed up the lookup by providing a shortcut for the long type name by providing aliases that reference the fully qualified name. When you create global object aliases, you defeat the purpose of the namespace containers. Each subsequent lookup can get a little more expensive for every item in the checked scope. The ideal time to create aliases is when something is going to be referenced frequently and you can alias it temporarily, when it will soon go out of scope and the alias will be cleaned up. If the code in Listing 4-6 were going to be run frequently, or if it contained many more calls to the types, it would probably be worth caching a reference to the type and avoid the repeated lookups. Creating a local alias is easy; just declare a variable and assign the type to it. Listing 4-7 demonstrates creating and using aliases for the Album and TributeAlbum types.

Listing 4-7
Image from book

var tributeAlbum = Wrox.ASPAJAX.Samples.TributeAlbum;
var album = Wrox.ASPAJAX.Samples.Album;

if(tributeAlbum.isInstanceOfType(anAlbum) == false) {
    alert("anAlbum is not a TributeAlbum");
}

if (tributeAlbum.isInstanceOfType(anotherAlbum) == true) {
    alert("anotherAlbum is a TributeAlbum");
}

if (tributeAlbum.inheritsFrom(album) == true) {
    alert("TributeAlbum inherits from Album");
}

if (album.inheritsFrom(tributeAlbum) == true) {
    alert("Album does not inherit from TributeAlbum");
}
Image from book

The AJAX library provides a method for explicitly calling a base method implementation. This is often used when a derived type wants to take the result from the base type and modify it before returning it to the caller. It is not limited to calling into the base type from a derived type’s implementation. You can also call a base method for an object. In a language like C++, you can cast an object to its base type to access a specific method implementation. Likewise, this pattern in JavaScript lets you access the base method even though JavaScript can’t support the casting semantic for this purpose. In Listing 4-8 (from CallBase.aspx), the TributeAlbum class adds an override for the get_artist method. It calls the base implementation and then prepends it with "TRIBUTE: " before returning it. This is again a slight modification to the previous example of the example using Album and TributeAlbum types.

Listing 4-8
Image from book
Wrox.ASPAJAX.Samples.TributeAlbum.prototype = {
    get_tributeArtist: function() {
        return this._tributeArtist;
    },
    set_tributeArtist: function(tributeArtist) {
        this._tributeArtist = tributeArtist;
    },
    get_artist: function() {
        return ("TRIBUTE: " +
        Wrox.ASPAJAX.Samples.TributeAlbum.callBaseMethod(this, "get_artist"));
    }
}
Image from book

There are also several auxiliary functions for inspecting the types you create with the AJAX Library. You may not use them much in your application development, but it is interesting to see what can be done with the dynamic environment of JavaScript. The Type.parse function takes the name of a type, resolves it, and creates an instance of it. The Type.isClass function checks that an object has been registered with the type system. You can traverse and inspect object inheritance chains using the getName and getBaseType methods. Listing 4-9 (from BaseType.aspx) demonstrates checking that the object is an instance of a type registered with the ASP.NET AJAX type system and then gets the name of the base type from which it derives.

Listing 4-9
Image from book

var typeString = "Wrox.ASPAJAX.Samples.TributeAlbum";
var typeCheck = Type.parse(typeString);
if(Type.isClass(typeCheck)) {
    alert(typeCheck.getBaseType().getName());
}
Image from book

Using Interfaces

Interfaces are a convenient way to define standard behaviors that other types can implement. Although JavaScript has no built-in support for interfaces, Microsoft has developed a way to mimic interfaces in JavaScript. As in .NET, an interface is a contract that states that the implementer of the interface must provide all of the functionality specified in the interface. The interface itself is only a specification and has no functionality of its own. When a type declares that it implements an interface, you are assured of the set of features it offers. Types can use interfaces to establish contracts for behavior that can then be enforced at runtime.

Interface definitions follow the same pattern as creating classes shown in earlier examples. The function name is the name of the interface. The prototype of the function is modified to add the interface members. The convention in defining interface members is to throw Error.notImplemented for each member, so any class that implements the interface then needs to override the interface members to provide real implementations or the exception will be thrown. Enforcing that the class actually overrides every member is the sort of thing that would be caught during compilation with a .NET Framework language, but JavaScript has to detect these problems at runtime.

Listing 4-10 (from Interface.aspx) defines the IProvideTrackInfo interface with methods for getting the track count and the track listing. The registerInterface method is available on the type itself, just as the class methods were.

Listing 4-10
Image from book
<script type="text/javascript">
function pageLoad(sender, args) {

Type.registerNamespace('Wrox.ASPAJAX.Samples');

Wrox.ASPAJAX.Samples.IProvideTrackInfo = function() {
    throw Error.notImplemented();
}
Wrox.ASPAJAX.Samples.IProvideTrackInfo.prototype = {
    get_trackCount: function() {
        throw Error.notImplemented();
    },
    get_tracks: function() {
        throw Error.notImplemented();
    }
 }
 Wrox.ASPAJAX.Samples.IProvideTrackInfo.registerInterface('Wrox.ASPAJAX.Samples.
IProvideTrackInfo');
}
</script>
Image from book

Obviously, when overriding the methods in a class that implements the interface, you wouldn’t want to call the base method and trigger the error!

Given a type, you can call getInterfaces to retrieve the complete set of interfaces that it implements. You can also check for a specific interface implementation by using the implementsInterface method on the type. Or you might need to check whether a type is itself an interface with isInterface. These are all operators on types, but there is also the isImplementedBy function, which takes an object instance and ascertains whether it is of a type that implements the interface.

For most development tasks, you will just define and use interfaces without using the methods for examining which objects implement what interfaces. But for more involved scenarios, the advanced functionality is useful. Listing 4-11 (also from Interface.aspx) shows the use of these methods on the IProvideTrackInfo interface. First, the Album class is modified to implement the interface.

Listing 4-11
Image from book
<script type="text/javascript">
Wrox.ASPAJAX.Samples.Album = function(title, artist) {
    this._title = title;
    this._artist = artist;
    this._trackCount = 0;
    this._tracks = null;
}

Wrox.ASPAJAX.Samples.Album.prototype = {
    get_title: function() {
        return this._title;
    },
    get_artist: function() {
        return this._artist;
    },
    get_trackCount: function() {
        return _trackCount;
    },
    get_tracks: function() {
        return _tracks;
    },
    set_tracks: function(tracks) {
        if(typeof(tracks) !== 'undefined'
            _trackCount = tracks.length;
            _tracks = tracks;
        }
    }
}

Wrox.ASPAJAX.Samples.Album.registerClass('Wrox.ASPAJAX.Samples.
Wrox.ASPAJAX.Samples.IProvideTrackInfo);

var album = new Wrox.ASPAJAX.Samples.Album('Rift',
var tracks = new Array(15);
tracks[0] = 'Rift';
tracks[1] = 'Fast Enough for You';
tracks[2] = 'Lengthwise';
tracks[3] = 'Maze';
tracks[4] = 'Sparkle';
tracks[5] = 'Horn';
tracks[6] = 'The Wedge';
tracks[7] = 'My Friend, My Friend';
tracks[8] = 'Weigh';
tracks[9] = 'All Things Reconsidered';
tracks[10] = 'Mound';
tracks[11] = "It's Ice";
tracks[12] = 'Lengthwise';
tracks[13] = 'The Horse';
tracks[14] = 'Silent In The Morning';

album.set_tracks(tracks);

alert(Wrox.ASPAJAX.Samples.Album.implementsInterface(
Wrox.ASPAJAX.Samples.IProvideTrackInfo) === true);
alert(Wrox.ASPAJAX.Samples.IProvideTrackInfo.isImplementedBy(album) === true);
alert(Type.isInterface(Wrox.ASPAJAX.Samples.IProvideTrackInfo) === true);
</script>
Image from book

Defining Enums and Flags

So far in this chapter, you have seen the Microsoft AJAX Library conventions for using namespaces, creating classes, and declaring interfaces as well as how to implement them. The last AJAX Library language feature I will cover is enumerations. The ASP.NET AJAX type system provides for defining enumerations and a specialized version of them used as combinable flags.

Enums let you establish a set of possible values. When using an enum, you know that it has to take on one of those values. Enum values are considered to be mutually exclusive. Colors are a typical example of an enum. If something is brown, it is not also green. (Sure, you could argue that something could be yellow and blue and thus green, but enums take a single value.) If the item is green, it is just green and nothing else from that enumeration.

In Listing 4-12 (from Enum.aspx), I define an enum for music genres. You can assign an album to be either Blues or Industrial, but you can’t make it be both Rock and Classical. I’m sure you can suggest something that is Classic Rock, but that is the point: an enum, by definition, can have only one value.

Listing 4-12
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <script type="text/javascript">
    function pageLoad(sender, args) {

        Type.registerNamespace('Wrox.ASPAJAX.

        Wrox.ASPAJAX.Samples.MusicGenre = function() {
            throw Error.invalidOperation();
        }
        Wrox.ASPAJAX.Samples.MusicGenre.prototype = {
            Blues:            1,
            Classical:        2,
            Electronic:       3,
            Folk:             4,
            Industrial:       5,
            Jazz:             6,
            NewAge:           7,
            HipHop:           8,
            Rock:             9,
            WorldFusion:     10
        }
        Wrox.ASPAJAX.Samples.MusicGenre.registerEnum('Wrox.ASPAJAX.Samples.
MusicGenre');

        var genre = Wrox.ASPAJAX.Samples.MusicGenre.Industrial;
        alert(Wrox.ASPAJAX.Samples.MusicGenre.toString(genre)); //displays
Industrial
        alert(genre === Wrox.ASPAJAX.Samples.MusicGenre.Industrial); //displays
'True'
        genre = 22; //assign an invalid value
        alert(Wrox.ASPAJAX.Samples.MusicGenre.toString(genre)); //displays an
error in debug mode
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager runat="server" ID="sm" ScriptMode="Debug"  />

    </div>
    </form>
</body>
</html>
Image from book

At the end of Listing 4-12, the genre is assigned an integer that is not part of the enum. The AJAX Library doesn't see the assignment of an integer to a variable but can check when the value is treated as an enum when calling toString on it. Normally, the invalid value is just displayed as an empty string, but Microsoft has added a terrific new feature to help you diagnose problems in JavaScript code: When you enable debugging by setting debug="true" in the compilation section of your web.config file (which is the typical setting on developer’s computers), more detailed information is produced. Figure 4-1 shows the dialog that Internet Explorer displays for this error when running in debug mode.

Image from book
Figure 4-1

Firefox also shows the error in the JavaScript console, if you have selected to show it from the Tools menu. The error message from Firefox is shown in Figure 4-2.

Image from book
Figure 4-2

Flags are a specialized type of enum. In fact, they are so similar that ASP.NET AJAX does not define a separate type for flags. You create and register a flags type the same way you create an enum, but the call to registerClass takes an additional parameter for indicating that the enum registration should be treated the same way as registering flags. By defining the enum as supporting flags, the behavior is distinctly different.

When an enum is declared as supporting flags, the values assigned to it are tracked as individual entries instead of as a single distinct value. If you use the logical OR operator (|) to combine enum values, what you have is the single enum value that is the sum of the parts.

genre = Wrox.ASPAJAX.Samples.MusicGenre.Blues |
Wrox.ASPAJAX.Samples.MusicGenre.Classical;
alert(Wrox.ASPAJAX.Samples.MusicGenre.toString(genre));

Combining Blues, with an enum value of 1, and Classical, with an enum value of 2, results in a variable with a value of 3. The enum for MusicGenre treats that as Electronic (the enum for a value of 3) and a toString call displays the same. I would be hard-pressed to come up with an example of music that is Electronic, Classical, and Blues all at the same time!

The combined value is still within the allowable range of values for the MusicGenre enum, so no error is encountered. However, since enums are ultimately supposed to designate a single value, you do get an error when defining a value that exceeds the defined range. If I decide that something is Electronic and Rock, the combination results in an error. Three plus nine is greater than the WorldFusion top value of ten. When debug is set to true, any attempt to display the value will result in an error.

Important

The debug version of the ASP.NET AJAX scripts provides valuable help in writing robust and resilient code. The debug scripts will alert you to passing invalid parameters, exceeding boundary conditions, and even using variables as types other than they are declared. However, in order to do this extra error-checking, the debug scripts are much larger and execute much more slowly than the release version. Always ensure that debugging is disabled on live deployments. ASP.NET provides a catch-all setting to ensure that optional settings are right for deployment. Setting retail to true overrides other debugging and trace settings that are useful during development but adversely affect performance at runtime. Chapter 9 addresses this issue in more detail.

<system.web>
<deployment retail="true" />
</system.web>

In Listing 4-13 (Flags.aspx), I define some music categories for which multiple values make sense. Music doesn’t necessarily need to be assigned to just one of these personal categories. There is music that I like to listen to while running and driving but that is too distracting for coding time. There is holiday music that I would listen to while hosting a party, but it would not provide motivation for running an extra mile or two.

Listing 4-13
Image from book
<%@ Page Debug="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>ASP.NET AJAX Flags</title>
    <script type="text/javascript">
    function pageLoad(sender, args) {

        Type.registerNamespace('Wrox.ASPAJAX.
        Wrox.ASPAJAX.Samples.MyCategory = function() {
            throw Error.invalidOperation();
        }

        Wrox.ASPAJAX.Samples.MyCategory.prototype = {
            Running: 1,
            Driving: 2,
            Dinner: 4,
            Holiday: 8,
            Entertaining: 16,
            Coding: 32
        }
        Wrox.ASPAJAX.Samples.MyCategory.registerEnum('Wrox.ASPAJAX.
Samples.MyCategory', true);

        var category = Wrox.ASPAJAX.Samples.MyCategory.Running |
            Wrox.ASPAJAX.Samples.MyCategory.Driving |
            Wrox.ASPAJAX.Samples.MyCategory.Coding;

        alert(Wrox.ASPAJAX.Samples.MyCategory.toString(category));

        var myCategory = Wrox.ASPAJAX.Samples.MyCategory;
        if(category & myCategory.Running)
            alert("Running")
        if(category & myCategory.Driving)
            alert("Driving");
        if(category & myCategory.Dinner)
            alert("Dinner");
        if(category & myCategory.Holiday)
            alert("Holiday");
        if(category & myCategory.Entertaining)
            alert("Entertaining")
        if(category & myCategory.Coding)
            alert("Coding");
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager runat="server" ID="sm" />

    </div>
    </form>
</body>

</html>
Image from book

The toString call on the flag reveals that the AJAX Library has tracked the set of combined flags. No longer is the result treated as a single value. Instead, the set of values are displayed: Running, Driving, and Coding. Notice that the individual values for flag elements follow the pattern of doubling. This enables the combination semantics in the same way that flags operate at the bit level.


Base Class Library

The AJAX Library takes a familiar set of features from the Base Class Library of the .NET Framework and brings it to JavaScript in the browser. It is by no means an equivalent set of functionality, but it does go a long way toward simplifying JavaScript coding tasks.

The String Class

Basic support for removing whitespace is added to JavaScript strings with new methods. The trim method performs the equivalent of trimBegin and trimEnd. The instance of the string is not modified by the trim calls. Instead, a copy of the string is returned with the requested change. Listing 4-14 (Trim.aspx) demonstrates using the trim, trimStart, and trimEnd functions.

Listing 4-14
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>ASP.NET AJAX Trim</title>
    <script type="text/javascript">

    function pageLoad(sender, args) {

        var one = ' leading
        var two = 'trailing whitespace ';
        var three = ' leading and trailing whitespace ';

        alert('.' +
        alert(one);  //the original string is not modified
        alert('.' +
        alert('.' +
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager runat="server" ID="sm"/>
    </div>
    </form>
</body>
</html>
Image from book

The trim methods are just shortcuts to what you can do with a regular expression. But they do provide a certain familiarity. This holds true of the beginsWith and endsWith methods too. You could write the functionality easily yourself, but having method names that are clear and easy to use makes the transition in and out of JavaScript easier.

Dates and Numbers

The complexities of formatting really come into play when dealing with dates and numbers. The ASP.NET AJAX Library adds format and localeFormat methods to the string, date, and number objects. The format and methods are key for effectively controlling output. Instead of concatenating pieces of text with variables, you can call String.format to have variables put into placeholders of a single string. The placeholders in the string are a pair of curly braces containing a variable number and optional format string. The number indicates which of the parameters following the string will be used. A variety of format strings like those available in the .NET Framework allow you to easily control how numbers are represented.

The localeFormat goes further by respecting culture-specific settings in formatting dates and numbers. You can establish the culture that the user prefers and know that your data is being formatted and displayed correctly in the browser.

Listing 4-15 (from Formatting.aspx) has examples of string formatting. The displayFavorites function injects a set of variables into a string. No format specifier is included. Then the {0:d3} format specifier is used to indicate that the number should be padded if it consists of fewer than three digits.

Listing 4-15
Image from book
<%@ Import Namespace="System.Globalization" %>
<%@ Import Namespace="System.Threading" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>ASP.NET AJAX String Formatting</title>
    <script runat="server" language="C#">
    protected override void OnLoad(EventArgs e) {
        Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture("de-DE");
    }
    </script>
    <script type="text/javascript">
    function pageLoad(sender, args) {

        var d = new Date();
        var message = String.localeFormat("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}",
            d.format("d"),
            d.format("D"),
            d.format("t"),
            d.format("T"),
            d.format("F"),
            d.format("M"),
            d.format("s"),
            d.format("Y") );
        alert(message);
    }

    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager runat="server" ID="sm" EnableScriptGlobalization="true" />

    </div>
    </form>
</body>
</html>
Image from book

The result of using the various date formats is shown in the pop-up window in Figure 4-3.

Image from book
Figure 4-3

Arrays

The Array type is used heavily in JavaScript coding. It can be treated as both a stack and a queue, and it allows for sparse arrays. Many basic data structures can use the Array as the underlying building storage mechanism. The AJAX Library adds a set of static functions to the Array type that improve its usability and make it more familiar. During a preview release of the AJAX Extensions, the additional functionality was added to the Array prototype. After careful consideration, this approach was changed in order to avoid incompatibilities with other JavaScript libraries.

If you are already used to the JavaScript Array methods, you may recognize that the new methods are wrappers to use the method names from the .NET Framework base class library. You also get the benefit of some very detailed analysis into the performance characteristics of working with arrays. For example, adding a single element to an array is best accomplished by using an indexer with assignment rather than calling Push. The AJAX Library provides the Array.add method to expose a less awkward method.

array[array.length] = something; //is more efficient than
array.Push(something);

Listing 4-16 (Arrays.aspx) demonstrates using the additional Array methods to work with an Array. The add, contains, and clear methods do what you would expect, but the static methods on the Array type take a little getting used to. The parse method takes a string that looks like an array listing and parses it for the individual elements. But the forEach method is most interesting. You pass the array and a callback method, and the forEach method is then automatically called for each element of the array. In this case, I am passing a reference to a function that simply displays the element of the array using the built-in alert method.

Listing 4-16
Image from book
<%@ Page Debug="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>ASP.NET AJAX Arrays</title>
    <script type="text/javascript">
    function arrayMethod(element, index, array) {
        alert(element);
    }

    function pageLoad(sender, args) {

        var array = new Array();
        Array.add(array, "Junta");
        Array.add(array, "Lawn Boy");

        if(Array.contains(array, "Junta")) {
            Array.clear(array);
        }
        array = Array.

        var items = ["Stash", "Hoist", "Tracking"];
        Array.addRange(array, items);
        Array.insert(array, 1, "Lawn Boy");

        for(var i = 0; i < array.length; i++) {
            alert(array[i]);
        }

        Array.forEach(array, arrayMethod);
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager runat="server" ID="sm" EnableScriptGlobalization="true" />
    </div>
    </form>
</body>
</html>
Image from book

The debate around extending the Array prototype centers on problems that can be encountered when iterating over the members of the type. Providing this functionality as static methods avoids those problems. The type can also be used as associative arrays, but the generally accepted pattern is to use the object itself for creating associative arrays. For compatibility, arrays are better suited for collections of objects or numeric index associations.

Listing 4-17 (Objects.aspx) illustrates using an object as an associative array.

Listing 4-17
Image from book
<%@ Page Debug="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>ASP.NET Associative Objects</title>
    <script type="text/javascript">
    function pageLoad(sender, args) {
        var releaseDates = new Object();
        releaseDates["Junta"] = new Date("May 8, 1989");
        releaseDates["Lawn Boy"] = new Date("September 21, 1990");
        releaseDates["Picture of Nectar"] = new Date("February 18, 1992");
        releaseDates["Rift"] = new Date("February 2, 1993");

        for(var property in releaseDates) {
            alert(property + " was released " + releaseDates[property]);
        }
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager runat="server" ID="sm" EnableScriptGlobalization="true" />
    </div>
    </form>
</body>
</html>
Image from book


Supporting Intellisense

Most people agree that one of the best features of Visual Studio is Intellisense. Having contextual information about class members during development makes the process more efficient. It eliminates a lot of the need to jump back and forth between coding and documentation. Not only does Intellisense give you information about what members are available, but it also provides abbreviated help about parameters and return values. This is a big improvement over the text editor I used on a monochrome monitor when I started my programming career.

A big part of the Intellisense feature is driven by the use of reflection by Visual Studio over the managed types referenced by the project. But this doesn’t work very well with a dynamic language like JavaScript. The approach that the ASP.NET team is using with JavaScript is to leverage XML doc comments. By leveraging XML doc comments, the next version of Visual Studio will be able to provide Intelisense for the ASP.NET AJAX Library as well as the new ASP.NET server controls written in C#, where reflection is more readily available.

XML doc comments provide information about JavaScript code to let the developer know how to use it. The first item typically included is a summary statement, as shown in Listing 4-18. The album object can proclaim to developers that it intends to be a repository for information about an album. This may seem obvious in this discussion, but as the number of classes grows and the use of intuitive names decreases, the need for an Intellisense aid is more apparent.

Listing 4-18
Image from book
<%@ Page Language="C#"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>ASP.NET AJAX Intellisense </title>
    <script type="text/javascript">
    function pageLoad(sender, args) {

        Type.registerNamespace('Wrox.ASPAJAX.Samples');

        Wrox.ASPAJAX.Samples.Album = function(title, artist) {
        /// <summary>Use this method to create a new Album.</summary>
        /// <param name="title" type="String" optional="false"
mayBeNull="false">The album title.</param>
        /// <param name="artist" type="String" optional="false"
mayBeNull="false">The album artist.</param>
        /// <returns type="Album">An Album object.</returns>
            this._title = title;
            this._artist = artist;
        }

        Wrox.ASPAJAX.Samples.Album.prototype = {
            get_title: function() {
        /// <summary>Album title accessor.</summary>
        /// <returns  type="String">Album title.</returns>
               return this._title;
            },
            get_artist: function() {
            /// <summary>Album artist accessor.</summary>
            /// <returns type="String">Album artist.</returns.
               return this._artist;
            }
        }

        Wrox.ASPAJAX.Samples.Album.registerClass('Wrox.ASPAJAX.Samples.Album');
        var anAlbum = new Wrox.ASPAJAX.Samples.Album("Round Room", "Phish");
        alert(anAlbum.get_title());
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
    </form>
</body>
</html>
Image from book

Listing 4-18 (Intellisense.aspx) repeats the Album class code from Class.aspx in Listing 4-4 and includes XML doc comments. For each function, there is a summary statement. If a function takes parameters, they are described in param entries, and finally there is information about the return type.

The parameter doc comments indicate whether including the parameter in the function call is optional and whether nulls are allowed. XML doc comments added to enums provide information about the individual fields. The album genre enum with comments is shown in listing 4-19 (also from Intellisense.aspx).

Listing 4-19
Image from book

Wrox.ASPAJAX.Samples.MusicGenre = function() {
    /// <summary>Classifies types of music into a distinct genre.</summary>
    /// <field name="Blues" type="Number" integer="true" static="true" />
    /// <field name="Classical" type="Number" integer="true" static="true" />
    /// <field name="Electronic" type="Number" integer="true" static="true" />
    /// <field name="Folk" type="Number" integer="true" static="true" />
    /// <field name="Industrial" type="Number" integer="true" static="true" />
    /// <field name="Jazz" type="Number" integer="true" static="true" />
    /// <field name="NewAge" type="Number" integer="true" static="true" />
    /// <field name="HipHop" type="Number" integer="true" static="true" />
    /// <field name="Rock" type="Number" integer="true" static="true" />
    /// <field name="WorldFusion" type="Number" integer="true" static="true" />
    throw Error.invalidOperation();
}
Wrox.ASPAJAX.Samples.MusicGenre.prototype = {
    Blues: 1,
    Classical: 2,
    Electronic: 3,
    Folk: 4,
    Industrial: 5,
    Jazz: 6,
    NewAge: 7,
    HipHop: 8,
    Rock: 9,
    WorldFusion: 10
}
Wrox.ASPAJAX.Samples.MusicGenre.registerEnum('Wrox.ASPAJAX.Samples.MusicGenre');
Image from book

For enumerations, the information looks somewhat redundant, but for Visual Studio to consistently pick up and display the correct Intellisense information, the comments should be complete. Another use of the XML doc comments is to include example usage. This can be helpful in providing the developer a quick sample without requiring him or her to switch to the more complete documentation. It is especially useful when working with complex functions for which some contextual information is needed to understand usage; seeing it used in one code snippet is often as helpful as many paragraphs of help information. Listing 4-20 is an example directly from the Error object of the ASP.NET AJAX Library. The popStackFrame function merits an example to aid in its use.

Listing 4-20
Image from book

Error.prototype.popStackFrame = function() {
    /// <summary>
    /// Updates the fileName and lineNumber fields based on the next frame in the
    /// stack trace. Call this method whenever an instance of Error is returned
    /// from a function. This makes the fileName and lineNumber reported in the
    /// FireFox console point to the location where the exception was thrown, not
    /// the location where the instance of Error was created.
    /// </summary>
    /// <example>
    /// function checkParam(param, expectedType) {
    ///     if (!expectedType.isInstanceOfType(param)) {
    ///         var e = new Error("invalid type");
    ///         e.popStackFrame();
    ///         return e;
    ///     }
    /// }
    /// </example>
Image from book


Errors and Debugging

Chapter 9 provides detailed information about testing and debugging ASP.NET AJAX applications, but this chapter on the AJAX Library would not be complete without a basic introduction to the debugging facilities. Debugging JavaScript can be quite challenging due to its dynamic interpreted nature. Part of the problem is that the tools currently available leave significant room for improvement. Another reason is that without being able to precompile JavaScript code, you often end up finding mistakes through runtime failures and errors. Further, runtime errors aren’t always helpful to assist you in identifying the problem, although the extra debug mode messages are helpful.

The Debug Trace Console

The Debug class makes tracking down issues easier. One of the facilities it supports is providing trace information from your code. You should always provide release and debug versions of your scripts, and your debug versions can include extra trace information. The output can go to Visual Studio, the Firefox console, or directly into a textarea on your page. (Again, detailed information and examples will be covered in Chapter 9.) Listing 4-21 (TraceConsole.aspx) shows that if you include a TextArea element in your page with an ID of TraceConsole, the Debug class will put trace information there.

Listing 4-21
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Trace Console</title>
    <script type="text/javascript">
    function pageLoad(sender, args) {
        Sys.Debug.trace("Trace information is sent to the TraceConsole");
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager runat="server" ID="sm" />
    <textarea id="TraceConsole" cols="512" rows="10" style="width:100%;border:solid
black 1px"></textarea>
    </div>
    </form>
</body>
</html>
Image from book

The results are shown in Figure 4-4. Notice that Internet Explorer, Firefox, and Opera do not format the date string exactly the same, but the debug output utilizes the TraceConsole on all three.

Image from book
Figure 4-4

Creating Errors

The ASP.NET AJAX Library provides an Error object for dealing with errors in a standard way across applications. The static create method of the Error object allows you to create a custom error by passing in a message and a JSON-formatted error info object. It also includes static methods for creating a set of standard errors that correspond to frequently used exceptions in the .NET Framework.

var e = Error.create("custom error", { name: "My Error"});
throw(e);

You may find that you don’t need custom errors, but you can still take advantage of the built-in error types. The common exceptions to throw are listed in the following table.

 Error.argument (paramName, message)

 Error.argumentNull (paramName, message)

 Error.argumentOutOfRange (paramName, actualValue, message)

 Error.argumentType (paramName, actualType, expectedType, message)

 Error.argumentUndefined (paramName, message)

 Error.invalidOperation (message)

 Error.notImplemented (message)

 Error.parameterCount (message)

 Error.argumentUndefined (paramName, message)

 Error.invalidOperation (message)

 Error.notImplemented (message)

 Error.parameterCount (message)

These error functions extend the native JavaScript object and work with it to provide additional debugging information. In Listing 4-22 (ErrorTypes.aspx), an argumentNull error is created and thrown. First, it is caught, and the Debug.traceDump method is called on it to examine the details of the error. Then it is thrown again without being caught. The error information is then bubbled up to the user, as you would normally expect. The trace output and browser dialog are shown in Figure 4-5.

Listing 4-22
Image from book
<script type="text/javascript">
function throwError(arg) {
    if(arg === null) {

        var e = Error.argumentNull("arg", "arg is required");
        e.popStackFrame();
        throw(e);
    }
}

function pageLoad(sender, args) {
    Sys.Debug.trace("Trace information is sent to the TraceConsole");
    try {
        throwError(null);
    }

    catch(e) {
        Sys.Debug.traceDump(e, "the error");
    }
    throwError(null);
}
</script>
Image from book

Image from book
Figure 4-5

Figure 4-6 shows the TraceConsole and a pop-up dialog offering the user the opportunity to debug.

Image from book
Figure 4-6

Validate Params

JavaScript does not have the access modifiers used in compiled languages. You can’t declare that something is private or protected. The only way that code is made inaccessible is for it to go out of scope during execution. This can lead to some clumsy coding. You may want something to be a private implementation detail, but JavaScript does not provide a clean way to do this. The ASP.NET AJAX Library does not provide a way to simulate access modifiers for JavaScript either. It does, however, borrow a pattern from the .NET Coding Guidelines: Members that the developer considers private are prefixed with an underscore.

Typically, since they are supposed to be private, you would probably not explore those methods, but the _validateParams method deserves mention. This method is included on the Function object and called dozens of times from the AJAX Library in the debug scripts. The calls reflect the information from the XML doc comments. The debug version of the AJAX Library methods checks for required parameters, checks the type of parameters, and validates where null arguments are not allowed.

Even if you aren’t seeing functional problems as you develop an application, I recommend enabling the debug mode temporarily before deployment and doing some testing. Do not leave debugging enabled during deployment, though. The parameter validation and debug information take a heavy toll on performance.

When an error is encountered during a call to _validateParams, an exception is thrown and you can then break into the debugger and find the source of the error. This can be very helpful in getting to the root of a problem or in finding subtle errors before deployment.


The Browser Object

ASP.NET has an extensive set of information about various browsers. There is a whole system for analyzing the headers sent in by a browser and producing a complex Browser Capabilities object that you can use from the page to tailor the application for the user. The control adapter architecture is based on this configuration system being able to specify where custom renderings should be used for particular browsers.

The AJAX Library defines some basic browser definitions and determines which one is being used. JavaScript code can then easily check the browser object and draw conclusions about any unique requirements of the browser.

Listing 4-23 (Browser.aspx) displays the information currently stored in the Browser object. It also shows how you can use an indexer to set custom properties on the Browser object. Remember that you can do this for any object in JavaScript. This is a case where extensibility is easy and intuitive. The different values returned by the Sys.Browser properties are shown in Figure 4-7.

Listing 4-23
Image from book
<%@ Page Debug="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <script type="text/javascript">
    function someMethod(argument) {
        if(argument === null) {
            alert("null");
        }
    }

    function pageLoad(sender, args) {
        Sys.Debug.trace("name = " + Sys.Browser.name);
        Sys.Debug.trace("version = " + Sys.Browser.version.toString());
        Sys.Debug.trace("hasDebuggerStatement = " +
Sys.Browser.hasDebuggerStatement.toString());
        if(Sys.Browser.agent === Sys.Browser.InternetExplorer) {
            Sys.Debug.trace("agent = Sys.Browser.InternetExplorer");
        }
        else if(Sys.Browser.agent === Sys.Browser.Firefox) {
            Sys.Debug.trace("agent = Sys.Browser.Firefox");
        }
        else if(Sys.Browser.agent === Sys.Browser.Safari) {
            Sys.Debug.trace("agent = Sys.Browser.Safari");
        }
        else if(Sys.Browser.agent === Sys.Browser.Opera) {
            Sys.Debug.trace("agent = Sys.Browser.Opera");
        }
        Sys.Browser.agent["supportsAudio"] = true;
        Sys.Debug.trace(String.format("{0} supportsAudio = {1}", Sys.Browser.name,
Sys.Browser.agent["supportsAudio"].toString()));
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:ScriptManager runat="server" ID="sm" EnableScriptGlobalization="true" />
    <textarea id="TraceConsole" cols="512" rows="10" style="width:100%;border:solid
black 1px"></textarea>
    </div>
    </form>
</body>
</html>
Image from book
Image from book
Figure 4-7


Summary

The ASP.NET AJAX Library brings familiar aspects of programming with the .NET Framework to JavaScript. You can create namespaces to group functionality. The AJAX Library provides a framework for using object-oriented development patterns from JavaScript. You can define interfaces and classes that implement them. You can extend types and call on the base implementation. The ASP.NET AJAX Library provides a set of exceptions in the Error class that work with the JavaScript Error object and provide a natural way to find and report problems. There is also debug support with verbose error messages to help you find bugs.

The AJAX Library also includes information about the browser object so that you can reliably customize your actions for the user’s environment. It also provides classes that assist in debugging. When running with debugging enabled, the ASP.NET AJAX Library will validate parameters passed to the framework classes and help you avoid errors.

The ASP.NET AJAX Library helps you to write JavaScript code in a familiar and intuitive way. Transitioning between VB.NET or C# and JavaScript is easier. You get a set of base class libraries that function in the browser the same way you see them work on the server. You get help working with strings and data that perform with high performance. The ASP.NET AJAX Library will also aid you in localizing your output for the application user.

Fundamentally, the ASP.NET AJAX Library is focused on making JavaScript programming easier. You can take advantage of ASP.NET on the server and leverage JavaScript in the browser. Although the languages you are using on client and server are different, you get a familiar set of functionaltiy that makes the transition back and forth easier.


Chapter 5: Using the ScriptManager

AJAX development centers on using more JavaScript. With increased use of JavaScript comes the need for better ways to manage, reference, localize (that is, provide different script versions for specific language and culture combinations), and transmit script code to the client browser. The ASP.NET ScriptManager is at the center of ASP.NET AJAX functionality. The ScriptManager is the key component that coordinates the use of JavaScript for the Microsoft AJAX Library. Custom controls also use it to take advantage of script compression and reliable loading, as well as for automatic access to localized versions of scripts.

In this chapter, you will see what the ScriptManager does and how you can control it. You will see how to use the ScriptManager to include your scripts, as well as how to use the AJAX Library scripts. You will also learn how to take advantage of the ScriptManager for accessing scripts embedded in a dll and show how it functions to retrieve localized script resources.

The Ubiquitous ScriptManager

A ScriptManager is required on every page that wants to use the AJAX Library. When the ScriptManager is included in the page, the AJAX Library scripts are rendered to the browser. This enables support for partial page rendering and use of the ASP.NET AJAX Client Library. Listing 5-1 (Bare.aspx) is a page with a barebones ScriptManager that does nothing more than render the Microsoft AJAX Library files to the browser.

Listing 5-1
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html
xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>ASP.NET AJAX ScriptManager</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server" />
    <div>
    </div>
    </form>
</body>
</html>
Image from book

Notice how the rendering changes when the ScriptManager is added to the page. You get five new script elements in the HTML sent to the browser.

The following HTML is part of what is sent to the browser when Bare.aspx is requested. You can see that querystring parameters in the script references are long. They include time stamp elements and unique hash identifiers for script that is registered dynamically. If you copy the path from the src attribute of the script element and paste it into your browser’s address bar, you will get the actual script resources returned from the server.

<script type="text/javascript">
<!-
var theForm = document.forms['form1'];
if (!theForm) {
    theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}
// -->
</script>

<script src="/chapter05/WebResource.axd?d=6kIHBZsykATBSq3fbEmsYQ2&amp;t=
632968784944906146" type="text/javascript"></script>

<script src="/chapter05/ScriptResource.axd?d=9x_HPpGK-eN7tqV0Ff_J6PdW6
RyjdfhhTabpkRyiakKJ_a_q_nueOi1SMgVHCnyemAE_Wi1zmQc6dppn1_ShJ3RT853s6OS8dnx
NpQibyXs1&amp;t=633054056223442000" type="text/javascript"></script>

<script src="/chapter05/ScriptResource.axd?d=9x_HPpGK-eN7tqV0Ff_J6PdW6
RyjdfhhTabpkRyiakKJ_a_q_nueOi1SMgVHCnyemAE_Wi1zmQc6dppn1_ShJ_clCUeTRolYBVlv
TyhBCrs1&amp;t=633054056223442000" type="text/javascript"></script>

The first script is rendered inline in the HTML without a callback to the server. There is no src attribute on the script element. It is the method for initiating a postback. The second is a script reference for the approximately 500 lines of JavaScript included in most ASP.NET pages. It is the fundamental script providing some of the essential functionality of ASP.NET 2.0, including callbacks, performing validation, and managing focus.

The next script reference is for the MicrosoftAjax Library. It is the same as the MicrosoftAjax.js file copied to the \Program Files\Microsoft ASP.NET\ASP.NET 2.0 AJAX Extensions\v1.0.61025\ MicrosoftAjaxLibrary\System.Web.Extensions\1.0.61025 directory when installing ASP.NET AJAX. The code can be used with other server technologies, as it is focused on enriched client-side development. There are debug and release versions of the script files, discussed later in this chapter. The resources are also embedded in the System.Web.Extensions.dll and then extracted by the HTTP request to the ScriptResource.axd handler. This is the JavaScript that supports the client-side type system and Base Class Libraries you saw in Chapter 4.

The third script reference is for the MicrosoftWebForms Library. This code is retrieved as a resource from the dll by default, but is also copied onto disk. These scripts provide support for the UpdatePanel and the lifecycle of events associated with using partial page rendering.

The other two script entries are rendered inline in the page. You can see this in the following code, which is again part of the page output from requesting Bare.aspx from Listing 5-1. The first is for the PageRequestManager that handles partial page updates. The second piece of script performs the primary startup of the client page lifecycle by initializing the application object.

<script type="text/javascript">
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('ctl02',
document.getElementById('form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls([], [], [], 90);
//]]>
</script>

<script type="text/javascript">
<!--
Sys.Application.initialize();
// -->
</script>

You have seen that including the ScriptManager in the page results in rendering the scripts necessary to use the client-side Microsoft AJAX Library and to take advantage of partial page rendering as well. It also renders the scripts necessary for initiating the lifecycle of JavaScript events.

Adding Script References

The ScriptManager element can contain a scripts collection for adding scripts to the page. The way you would typically include a separate file of JavaScript in a page would be to use the HTML script element. You set the type attribute to "text/javascript" and the src attribute to the path of the JavaScript file, as shown in the following code.

<script type="text/javascript" src="script.js"></script>

Instead of writing the script element directly, the script can be added to the set of scripts that the ScriptManager controls using a ScriptReference element. By including it this way, I am assured that it will be loaded at a point when the ASP.NET AJAX Library is available. This is shown in Listing 5-2 (ScriptReference.aspx).

Listing 5-2
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>ScriptManager</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    <Scripts>
        <asp:ScriptReference Path="sample.js" />
    </Scripts>
    </asp:ScriptManager>
<div>
</div>
</form>
</body>
</html>
Image from book

You would expect that requesting this page would result in the same output as running Bare.aspx from Listing 5-1 (with one additional script element), but this is not exactly the case. After the initial request for the page, everything appears to be the same, but during partial page updates when asynchronous updates are happening and scripts are being loaded, a JavaScript error is encountered, as shown in Figure 5-1.

Image from book
Figure 5-1

A call to Sys.Application.notifyScriptLoaded is required so that the ScriptManager can provide the client lifecycle reliably on various browsers. Not all browsers provide an event in their DOM for detecting this automatically. The script would actually run fine without it, but to ensure cross-browser support, the ScriptManager requires the call. Listing 5-3 (Sample.js) shows the recommended way of modifying scripts so that they can provide the required call to the Microsoft AJAX Library when it is present, and still function reliably when used separately.

Listing 5-3
Image from book
// filesystem based script resource

function identityFunction(arg) {
    return arg;
}

if(typeof('Sys') !=='undefined')Sys.Application.notifyScriptLoaded();
Image from book

Rather than directly making the call, I first need to check that the Sys namespace is defined. The script can then be included in a page where the ASP.NET AJAX Library is not being used without causing an error.

Setting the ScriptMode

The ScriptManager and ScriptReferences both allow you to set the ScriptMode. The default value of this enum is Auto. The other possible values are Release, Debug, and Inherit. The ScriptMode determines whether release or debug versions of the scripts are used. When set to Auto, the determination is primarily the result of server settings. Debug scripts are used when debug is set to true in the compilation section of the web.config file, or when the debug page directive is set to true. The deployment setting in the configuration file trumps all of the other settings. When retail is set to true, all other debug settings are treated as false and you won't get debug versions of the scripts.

ASP.NET AJAX includes release and debug versions of the scripts, but for ScriptReferences that refer to files on disk, the ScriptManager does not assume that you have provided debug versions of the script, so the path given is used regardless of the server’s debug setting. The Auto setting for ScriptMode is almost identical to the Inherits value, except for this behavior. Inherits will take the value directly from the server configuration. The ScriptManager doesn’t look to configuration or page settings when the ScriptMode is set directly to Release or Debug.

The pattern you use to provide debug versions of scripts is to add .debug to the filename before the .js suffix. The name the ScriptManager would assume for the debug version of Sample.js would be Sample.debug.js. The filenames are not validated, so specifying the debug version of a script file that doesn’t exist will result in an error like the one shown in Figure 5-2. Again, this error is only produced when script loading is validated during asynchronous postbacks, not during the initial loading of the page.

Image from book
Figure 5-2

The choice of using .debug in the script name is arbitrary, but it does allow automation in finding the right version of scripts on disk, as well as when they are embedded as resources in a dll. You won’t have to keep swapping files during development and deployment. You can set the debug mode you want and get the right version for the scripts you want to debug. For file-based script resources, you toggle between the release and debug versions by setting the ScriptMode attribute to Debug directly on the ScriptReference.

Embedding Script Resources

There are some advantages to embedding JavaScript files as resources in a dll and deploying them that way. You don’t have to wonder whether someone has modified the scripts on the server. You can maintain a set of release and debug scripts in a single location and use the ScriptManager to dynamically switch between versions. And once deployed, you have version information on the scripts, allowing for easier maintenance and servicing.

One key difference exists in how the ScriptManager treats scripts retrieved from the filesystem compared with those embedded as a resource in a dll. When the path to a script is used, the ScriptManager provides a callback to the ScriptResource handler, which retrieves the contents. The script itself is not modified (no extra calls are injected). When a script is retrieved as an embedded resource, however, the ScriptManager injects the call to Sys.Application.notifyScriptLoaded for you automatically. This allows you to start using scripts that you already have with ASP.NET AJAX without having to rebuild the dlls.

In Visual Studio, you first create a class library project. You can do this from your existing web application by right-clicking the Solution name in the Solution Explorer window. From the Add New Project dialog, you can add a new project by selecting the Class Library template (see Figure 5-3). Alternatively, you can choose to create a new Project from the File menu. In the dialog where you select the Class Library type you can also choose to add this project to the current Visual Studio Solution.

Image from book
Figure 5-3

You create the script files as you normally would, by adding JScript files to the project. Remember that using the naming convention of adding .debug into the filename for the debug version of a script enables automatic switching between release and debug versions at runtime. To embed the scripts into the resulting dll, you set the Build Action in the Properties pane for the script file to Embedded Resource, as shown in Figure 5-4.

Image from book
Figure 5-4

In this example, I have added embeddedSample.js with a function called doubleArg that just returns double what it is passed. Listing 5-4 (embeddedSample.debug.js) includes some error checking. For some functions, you want to do parameter validation in release scripts, but for many situations, you just want the extra checks during development and testing.

Listing 5-4
Image from book

function doubleArg(arg) {
    if(typeof(arg) === 'undefined'){
        throw Error.argumentUndefined('arg');
    }
    if(arg === null) {
        throw Error.argumentNull('arg');
    }
    if((arg % 0) === 0) {
        throw Error.argumentOutOfRange('arg',arg);
    }

    return 2*arg;
}
Image from book

To be able to add a ScriptReference for the embeddedSample script, you need to define the WebResource for the project. The WebResource attribute is in the System.Web.UI namespace. The attribute is used in the code of your project to include the script into the compiled dll. The project needs a compile-time reference to the System.Web assembly to compile. Right-click the References entry of the Solution Explorer in Visual Studio and select Add Reference. Figure 5-5 shows the dialog where you can select System.Web.

Image from book
Figure 5-5

The WebResource is added to the AssemblyInfo.cs file, which is in the Properties directory of the class library project. The WebResource attribute tells ASP.NET the names and types of resources available from assemblies in your web project. In this case, there are two resources, and both of them are JavaScript files to be embedded in the assembly.

[assembly: WebResource("MyScripts.embeddedSample.js", "text/javascript")]
[assembly: WebResource("MyScripts.embeddedSample.debug.js", "text/javascript")]

If you create the class library as a project within your web project in Visual Studio, you can modify the properties of the project to have the resource assembly copied into the bin directory of your web application. If it is a separate project, you will need to explicitly set the output location or copy the dll into your application manually.

Tip

To declare the dependency on the separate project, right-click on the Web Project in the Solution Explorer and select the Add Reference option. From there, you can select the project with the embedded resources. Also, you have to select the script file in the Solution Explorer of Visual Studio, and in the Property Grid set the Build Action to the Embedded Resource value. This is the same place where you can choose to copy the output to a specific location when the dll is compiled.

Now you have a dll with script resources embedded, and can include them in your page with a ScriptReference. Listing 5-5 (EmbeddedReference.aspx) is a page that includes the script and calls to the doubleArg function with valid and invalid parameters.

Listing 5-5
Image from book
<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>ScriptManager</title>

</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1" >
    <Scripts>
        <asp:ScriptReference Name="MyScripts.embeddedSample.js"
Assembly="MyScripts" />
    </Scripts>
    </asp:ScriptManager>
</div>
</form>
</body>
</html>
<script type="text/javascript">
function pageLoad() {
    alert(doubleArg(3));
    alert(doubleArg());
}
</script>
Image from book

When debugging is not enabled for the page, the browser displays the number 6 and NaN. The second call to the function does not include an argument, and the release script just tries to double it and returns the “Not A Number” JavaScript value. When using the debug version of the script, however, you get the benefit of running the debug version of the script, and a better error message is displayed, explaining that the argument cannot be undefined.

You saw when using the path attribute of a ScriptReference to load a script from a file that the ScriptManager does not switch automatically between debug and release versions of the script. And when using the name and assembly attributes to specify loading of an embedded script, it will pick up the right version based on the server setting. If you do not provide a debug version of the script, the ScriptManager will notice this and fall back to the release version. It is not an error to skip inclusion of debug script resources.

You can include all three attributes - name, assembly, and path - in a single ScriptReference ele-ment and get a slightly different behavior still. The path will be used to retrieve the script from disk, but the determination of whether or not a debug version of the script is available is made by looking at the embedded resources. If you have a debug embedded resource and choose to get the script from disk, it is an error to try and retrieve the debug version if it does not exist on disk. The ScriptManager will not fall-back from the filesystem to the embedded resource.

Script Localization

The .NET Framework has good support for providing and using localized resources in .NET applications. This is a feature that has been lacking in JavaScript. ASP.NET AJAX makes it possible to provide localized string resources and have the correct language used automatically at runtime.

It follows the same principle as the previous example of embedded script resources, but extends it to automatically generate a string resource class. The following JavaScript function uses the shortText and longText properties of the Res class.

function LocalizedMessage() {
    alert(Wrox.ASPAJAX.Samples.LocalizedMessage.Res.shortText);
    alert(Wrox.ASPAJAX.Samples.LocalizedMessage.Res.longText);
}

But you haven’t defined the Res class. Instead, the ScriptResource attribute is used along with the WebResource attribute to instruct the ScriptManager to create the class. The WebResource attribute is still required to specify the resource name and its type. Then the ScriptResource attribute is added with the name of the script resource, along with the name of the string resources to use and the name of the class to create in JavaScript for use in the browser. This is done in the AssemblyInfo.cs class of the project as shown in the following.

  [assembly: WebResource("MyScripts.LocalizedMessage.js", "text/javascript")]
[assembly: ScriptResource("MyScripts.LocalizedMessage.js",
    "MyScripts.LocalizedMessage",
    "Wrox.ASPAJAX.Samples.LocalizedMessage.Res")]
Image from book
Figure 5-6

The name of the .resx files you create is key, as it is used to determine the culture of the resources contained in that file. You can define shortText and longText keys in the LocalizedMessage.resx file and then create a LocalizedMessage.fr-FR.resx file to house French translations of the messages. In this case, fr means the French language, and FR means the French culture. In some cases, a given language can have several culture variations, for example, American English versus British English. Figure 5-7 shows the French resource file. As you can see, it really is just a set of name-value pairs being established for cultures that will be accessed in the application.

Image from book
Figure 5-7

To use localized resources, you do not have to provide versions for every possible culture known to the .NET Framework. A request for a culture that does not have localized versions of the strings will fall back to the default strings instead. To enable localization on the ScriptManager, you must set the EnableLocalization property to true. It is false by default.

Another default setting of ASP.NET needs to be changed to make effective use of the localization feature. The ScriptManager will provide the localized resources in the culture setting under which the page is running. By default, this does not reflect the user’s language setting in the browser. The browser will send with each web request an ordered list of language preferences. By setting the UICulture to Auto, ASP.NET will take the top entry from the list of languages and use that culture to execute the request. Note that it does not try subsequent entries in the list if the first one is not a valid culture on the server. Listing 5-6 (LocalizedResource.aspx) is a page that sets the culture, enables localization, includes the ScriptReference for the localized file, and calls the localizedMessage function, which uses the resources.

Listing 5-6
Image from book
<%@ Page Language="C#" UICulture="Auto" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>ScriptManager</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1"
EnableScriptLocalization="true">
    <Scripts>
        <asp:ScriptReference Assembly="MyScripts"
Name="MyScripts.LocalizedMessage.js" />
    </Scripts>
    </asp:ScriptManager>
<div>
</div>
</form>
</body>
<script type="text/javascript">
function pageLoad() {
    LocalizedMessage();
}
</script>
</html>
Image from book

Now, if the language preference in the browser is English, the page will display Attention messages in English. The options dialog of your browser allows you to add, remove, and reorder your language preference. Figure 5-8 shows adding French to the list of preferred languages. Remember that ASP.NET will only try the topmost item from the preferred languages list. When French is the most preferred language, the UICulture for the thread processing the server request is switched to French, and the ScriptManager selects the French resource strings.

Image from book
Figure 5-8

Script Globalization

Script localization allows you to provide for specific translations of text for use in JavaScript in the browser. Script globalization is the ability to format and parse data using a specific culture. For example, it is customary in U.S. English for dates to be formatted as month, followed by the day and then the year (8/21/1993), but in many other cultures, the day of the month is presented first (21/8/1993). The ScriptManager will populate a CultureInfo object, with the data from the culture being used to process the page on the server when EnableGlobalization is set to true. It is false by default. For script localization, you change the Page UICulture property to Auto, but for globalization you use the Culture property. UICulture is for language choice, while Culture is for date and number parsing and formatting.

In Listing 5-7 (Globalization.aspx), I show the string formatting functions using String.localeFormat. This version of the formatting API will respect the current culture setting in the browser. With globalization, you can get language-specific formatting in the browser without needing to provide localized resource files in your application.

Listing 5-7
Image from book

<%@Page Language="C#" Culture="Auto" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>EnableGlobalization</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1"
EnableScriptGlobalization="true" >
    </asp:ScriptManager>
<div>
    <input type="text" id="textbox" value="something"/>
</div>
</form>
</body>
<script type="text/javascript">
function pageLoad(sender, args) {
    var d = new Date();
    var message = String.format("With {0} culture date formats
are:\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n{8}",
        Sys.CultureInfo.CurrentCulture.name,
        d.localeFormat("d"),
        d.localeFormat("D"),
        d.localeFormat("t"),
        d.localeFormat("T"),
        d.localeFormat("F"),
        d.localeFormat("M"),
        d.localeFormat("s"),
        d.localeFormat("Y") );
    alert(message);
}
</script>
</html>
Image from book

Service References

Much of the emphasis of richer application development centers on leveraging web services through asynchronous communications with the server. ASP.NET Ajax makes this easy. You can declare references to web services within the ScriptManager in much the same way that the previous examples do for scripts. Suppose you want to mark the time that some event occurred on the client. You might provide a web service like the one in Listing 5-8 (Servertime.asmx) so that you are not relying on the local time of the user’s machine.

Listing 5-8
Image from book
<%@ WebService Language="C#" Class="Wrox.ASPAJAX.Samples.TimeOfDay" %>

using System;
using System.Web;
using System.Web.Script.Services;
using System.Web.Services;
using System.Web.Services.Protocols;

namespace Wrox.ASPAJAX.Samples
{

    [ScriptService]
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class TimeOfDay : System.Web.Services.WebService
    {
        [WebMethod]
        public string TheTime()
        {
            return DateTime.Now.ToString();
        }
    }
}
Image from book

By including the new ScriptService attribute on the class declaration, you declare to ASP.NET that it should provide a JavaScript proxy class for use in calling the web service. If you navigate directly to the .asmx file that contains the web service, ASP.NET provides a page that allows you to invoke the methods directly from the browser without writing any code. In a similar vein, if you navigate to the .asmx file in the browser and append /js to the filepath, ASP.NET will return the proxy code necessary to invoke the method directly from script.

The ScriptManager will generate a dynamic script element for the proxy code associated with each ScriptReference it contains. You can optionally set the InlineScript property to true to have the proxy code rendered directly in the page. Listing 5-9 (ServiceReference.aspx) includes a ScriptReference to the TimeOfDay web service. The pageLoad function first sets the timeout to 1000 milliseconds from the default value, where there is no client timeout. The code then invokes the web service providing the names of callback functions to invoke for success or failure.

Tip

Web browsers limit the number of simultaneous connections they make back to the server, typically to two. So, if you have a lot of ScriptReference elements in a page, you may want to consider having some select ones rendered inline to avoid contention for multiple concurrent requests to download the proxies. However, the tradeoff is that inline scripts are not cached by the browser and bloat the size of the page. Large, frequently used scripts should be external references to take advantage of browser caching.
Listing 5-9
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
 <title>ASP.NET AJAX Service Reference</title>
</head>
<body>
 <form id="form1" runat="server">
 <asp:ScriptManager runat="server" ID="ScriptManager1">
 <Services>
 <asp:ServiceReference Path="ServerTime.asmx" />
 </Services>
 </asp:ScriptManager>
 <div>
 </div>
</form>
</body>
<script type="text/javascript">
function pageLoad() {
  Wrox.ASPAJAX.Samples.TimeOfDay.set_timeout(1000);
  Wrox.ASPAJAX.Samples.TimeOfDay.TheTime(success, failed, "arbitrary info for the
callback")
}

function success(result, userContext) {
 alert(result + " " + userContext);
}

function failed(result) {
    var message = String.format("statusCode={0}\r\nexceptionType
={1}\r\ntimedOut={2}\r\nmessage={3}\r\nstackTrace={4}",
    result.get_statusCode(),
    result.get_exceptionType(),
    result.get_timedOut(),
    result.get_message(),
    result.get_stackTrace());
    alert(message);
}
</script>
</html>
Image from book

The third argument passed to the proxy for invoking the web service is called the userContext. It is not sent to the server but is passed to the completion callback function. This allows you to carry extra data to your callback that is not involved in the web service call without consuming resources to roundtrip it to the server.

Even code that runs correctly almost all of the time can encounter something unexpected. Listing 5-10 (from ThrowError.asmx) demonstrates this by throwing an exception to the web service code from Listing 5-8. If an error is encountered and an exception thrown, or if the timeout is exceeded, the error callback is called instead of the success completion function.

Listing 5-10
Image from book
<%@ WebService Language="C#" Class="ThrowError" %>

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class ThrowError  : System.Web.Services.WebService {

  [WebMethod]
  public string TheTime(){
      throw new Exception("failed miserably");
      return DateTime.Now.ToString();
  }
}
Image from book

The object passed to the error handler will provide a JavaScript call stack when an exception is thrown. It also carries any exception message, what the HTTP status code was, and whether or not the error was caused by a timeout. The result of throwing this exception is shown in Figure 5-9.

Image from book
Figure 5-9


ASP.NET Application Services

ASP.NET AJAX provides a client framework that simplifies JavaScript development, but, as you have seen, it also provides server-focused solutions for easily accessing web services and localizing script resources. In addition, ASP.NET AJAX makes it easy to leverage the user authentication and profile management features of ASP.NET 2.0.

Authentication services allow you to remotely check user credentials in JavaScript against a central user repository. ASP.NET provides a default database and authentication scheme that you can use. Profile services allow you to store and retrieve data for each authenticated user on the server. ASP.NET AJAX makes this information available from JavaScript running in the browser.

Forms Authentication

ASP.NET Forms Authentication allows you to store user credentials in a database or other backend without creating individual Windows accounts for every user. ASP.NET AJAX makes it possible to authenticate users against the server storage directly from JavaScript in the browser.

First, Forms Authentication must be enabled for the application that requires credentials using the authentication section under system.web in the application configuration file. The following configuration data from a web.config file shows setting the authentication mode to Forms and defining that cookies should be used to store the authentication ticket. ASP.NET supports carrying the authentication ticket in the URL of requests, but when authenticating from JavaScript, cookies must be used.

<authentication mode="Forms">
  <forms cookieless="UseCookies" />
</authentication>
<authorization>
  <deny users="?" />
  <allow users="*" />
</authorization>

That configuration will enable Forms Authentication using cookies. The deny element uses the question mark to indicate that the anonymous user is not allowed. The allow element uses the asterisk to allow all authenticated users.

That is enough to get Forms Authentication going for a web request, but accessing it from JavaScript also requires explicitly setting it in the system.web.extensions configuration section. This is shown in the following configuration entry from an ASP.NET AJAX application.

<system.web.extensions>
    <authenticationService enabled="true" />
</system.web.extensions>

By default, ASP.NET will create a database using Microsoft SQL Express for development. You can use the aspnet_regsql tool to populate your existing database with the tables and schema for using the default authentication providers.

With the configuration in place, you can call login and logout against the ASP.NET Authentication service from JavaScript. The login method requires a username and password. You can also specify that the authentication cookie be persisted beyond when the browser is closed, any custom data that should be available in the callback, and the redirectUrl to use upon successful login. Of course, you can provide callback functions for success and failure.

Listing 5-11 (Login.aspx) includes code for logging a user in and out in the click handler of a button. The logout function has fewer parameters: the redirectUrl to use, completion callbacks for logout and a failed attempt to logout, and the userContext object to pass to the callbacks. The userContext ele-ment is not sent to the server and then returned to the client. Instead, the AJAX infrastructure sets up the call to the server, puts the custom data aside, and passes it to the completion function when the call to the server is complete.

Listing 5-11
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
 <title>ASP.NET AJAX Login</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ID="ScriptManager1" >
</asp:ScriptManager>
<div>Username: <input type="text" name="username" /></div>
<div>Password: <input type="password" name="password"</div>
<div>
    <input type="button" id="loginButton" value="Login"/><br />
    <input type="button" id="logoutButton" value="Logout" />
</div>

<div id="Results"></div>
  </form>
</body>
<script type="text/javascript">
function pageLoad() {
    document.getElementById('loginButton')
    $addHandler($get('logoutButton'),
}

function login() {
    var u = $get('username').value;
    var p = $get('password').value;
    var isPersistent = false;
    var customInfo = null;
    var redirectUrl = null;
    Sys.Services.AuthenticationService.login(u, p, isPersistent, customInfo,
redirectUrl, loginCompleted, loginFailed);
}

function loginCompleted(result, context, methodName) {
    if(result) {
        alert("Successfuly logged in.");
    }
    else {
        alert("Failed to login.");
    }
}

function loginFailed(error, context, methodName) {
    alert(error.get_message());
}
function logout() {
    Sys.Services.AuthenticationService.logout('Default.
}
</script>
</html>
Image from book

Notice in Listing 5-12 (Authenticate.asmx) that there is an AuthenticationService element without any attributes. It is actually not required for this example. When using the built-in Authentication service, it is enabled through configuration, as you’ve seen. But you see that you have the flexibility to customize the membership feature beyond what is provided natively by ASP.NET. The AuthenticationService element allows you to set the path of the service to a custom web service: <AuthenticationService path="authenticate.asmx" />. You can write your own service with the Login and Logout method signatures required and use it to validate credentials against any custom middle tier or backend data store that you want. Listing 5-20 is an example of a service with the appropriate method signatures so that the JavaScript AuthenticationService classes will successfully call the custom implementation.

Listing 5-12
Image from book
<%@ WebService Language="C#" Class="Wrox.ASPAJAX.Samples.Authenticate" %>
using System.Web;
using System.Web.Security;
using System.Web.Services;
using System.Web.Script.Services;

namespace Wrox.ASPAJAX.Samples
{
    [ScriptService]
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Authenticate : System.Web.Services.WebService
    {
        [WebMethod]
        public bool Login(string userName, string password)
        {
            //storing passwords in code is a BAD security practice
            if (userName == "Matt" && password == "MyPassword!")
            {
                FormsAuthentication.SetAuthCookie(userName, false);
                return true;
            }
            return false;
        }

        [WebMethod]
        public void Logout()
        {
            FormsAuthentication.SignOut();
        }
    }
}
Image from book

Profile Service

The ASP.NET profile feature allows you to automatically store and retrieve data for each user of a web application. It can even be enabled for anonymous users that have not been authenticated. ASP.NET will issue a unique identifier to the user to be able to retrieve the profile data on subsequent requests. The service must be enabled for ASP.NET and additionally for JavaScript access from the browser. In your application’s web.config file, you use the profile section inside system.web to enable the service. This is shown in the following example section from a web.config file.

<system.web>
<anonymousIdentification enabled="true"/>
<profile>
<properties >
<add name="favoriteArtist" type="System.String"/>
<add name="favoriteAlbum" type="System.String"/>
</properties>
</profile>
</system.web>

There are no default properties tracked for personalization. They must be defined inside the properties element of the profile configuration section. I have defined favoriteArtist and favoriteAlbum string properties. Because space is consumed in the database to track the personalization data for each user, it is disabled by default for anonymous users. Once it is enabled with the anonymousIdentification ele-ment, every unique visitor to the web application will be issued a unique identifier, and an entry will be created for them in the database.

The profile service must be enabled separately for direct client access. This is so that you have to opt in to the ability to retrieve and update personalization data remotely. Without requiring users to be authenticated, it is possible for spoof requests to be sent to the application and modify the data.

The system.web.extensions section has a profileService section where it is enabled. You also define what profile properties should be readable and which should be writeable. When accessed directly from the server, all properties are read-write, but you can limit the access further for JavaScript access. The following configuration element from the <system.web.extensions> section of the web.config file sets up both properties for read and write access.

<profileService enabled="true"
readAccessProperties="favoriteArtist,favoriteAlbum"
writeAccessProperties="favoriteArtist,favoriteAlbum" />

Now I can include JavaScript code in a page that directly stores and retrieves user personalization data. In Listing 5-13 (Profile.aspx), I first populate several items in the user profile from code running on the server. Once the data is stored in the profile, it will be available automatically in subsequent requests. In the ProfileService element of the ScriptManager, the LoadProperties attribute can be set to a comma-delimited list of Profile properties that should be prepopulated in the JavaScript sent to the browser. This is useful for profile information used often, and you want to avoid having to call back to the server later to retrieve it. This code tells it to load the favoriteArtist property but not the favoriteAlbum.

Listing 5-13
Image from book
<%@ Page Language="C#"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>Profile Service</title>
</head>
<script runat="server" language="c#">
void Page_Load() {
    if(!this.IsPostBack) {
        Profile.favoriteArtist = "Phish";
        Profile.favoriteAlbum = "Rift";
    }
}
</script>
<body>
  <form id="form1" runat="server">
  <asp:ScriptManager runat="server" ID="ScriptManager1" >
  <ProfileService LoadProperties="favoriteAlbum" />
  </asp:ScriptManager>
  <div>
  <input type="button" value="Get Artist" id="showArtist" /><br />
  <input type="button" value="Get Album" id="showAlbum" /></div>
  </form>
</body>
<script type="text/javascript">
function pageLoad() {
    $addHandler($get('showArtist'),
    $addHandler($get('showAlbum'),
}

function displayArtist() {
    var p = Sys.Services.ProfileService;
    p.load(["favoriteArtist"], loadCompleted, loadFailed);
}

function loadCompleted(count) {
    alert(Sys.Services.ProfileService.properties.favoriteArtist);
}

function loadFailed() {
    alert('load failed');
}

function displayAlbum() {
    alert(Sys.Services.ProfileService.properties.favoriteAlbum);
    Sys.Services.ProfileService.properties.favoriteAlbum = "Round Room";
    Sys.Services.ProfileService.save(["favoriteAlbum"]);
}
</script>
</html>
Image from book
Tip

ASP.NET allows you to plug in your own provider so you can easily customize where the data is stored. For these examples, I am using a custom Profile data provider that just stores the data in memory temporarily rather than setting up a database.

There are two buttons on the page. One is the showArtist button and the other is showAlbum. The click handler for the showArtist button accesses the profile property directly and displays it. Because the album information was not prepopulated, the showAlbum handler has to perform a callback to get the data. It sets up success- and failure-completion functions to be called when the callback is complete. The success callback updates the album to a new value. After a postback, the showAlbum handler will retrieve the new value from the Profile Service.

The Profile Service example makes use of basic string properties, but you can also easily use more complex types. ASP.NET AJAX uses JSON (JavaScript Object Notation) serialization to move data back and forth between client and server. If you define an Album type with properties for AlbumName and Artist, the Profile Service will automatically manage the type in .NET code on the server and in JavaScript in the browser. Album.cs in Listing 5-14 is such a type.

Listing 5-14
Image from book

using System;
using System.Web;
using System.Web.UI;

namespace Wrox.ASPAJAX.Samples {

public class Album {
    private string _artist = String.Empty;
    private string _name = String.Empty;

    public Album() {
    }

    public Album(string artist, string name) {
        _artist = artist;
        _name = name;
    }

    public string Artist {
        get {
            return _artist;
        }
        set {
            _artist = value;
        }
    }

    public string AlbumName {
        get {
            return _name;
        }
        set {
            _name = value;
        }
    }
}
}
Image from book

Adding this type to the Profile section of web.config makes the type available for inclusion in the LoadProperties attribute of the ProfileService element of ScriptManager. The Profile Service will then serialize the type as a JSON object both when reading and writing the property. Instead of setting each property individually, as shown here:

Sys.Services.ProfileService.properties.nextAlbumToBuy.Artist = 'Phish';
   Sys.Services.ProfileService.properties.nextAlbumToBuy.AlbumName = 'Round Room';

you can assign it as a JSON object and know that the Profile Service will be able to serialize it and send it to the server for deserialization.

Sys.Services.ProfileService.properties.nextAlbumToBuy = { Arist: 'Phish',
AlbumName: 'Round Room' };

One thing that caught me off guard in building this sample was the behavior of the success callback for saving Profile properties. The success callback will be called even when there is a problem updating all of the Profile properties. The success callback has an argument that is the count of the number of properties that were successfully updated. If there are problems updating some, or even all, of the properties included, the success callback will be called unless there is some fundamental problem with the service call itself.


Error Handling

No matter how much I believe that my code is going to handle things, inevitably something happens and an exception is thrown. It might be a timeout accessing a remote service or an offline database in the middle tier. On occasion, I haven’t accounted correctly for all of the edge cases that can occur when processing user input. Adding asynchronous callbacks to more of the ASP.NET application provides additional challenges in handling errors in a user-friendly way.

Allowing Custom Errors

If your user encounters an error with your ASP.NET application, you may be taking advantage of ASP.NET custom errors to redirect the user. You can provide a page with a look and feel consistent with the rest of the site that provides something more user friendly than a developer-focused error message. At the same time, you might log some information using tracing functionality so that you are aware of the frequency with which users are encountering errors. Then you can follow up to try and improve the experience and prevent the error from occurring in the future.

With asynchronous postbacks and partial rendering, an exception can go back to the script that initiated the callback, and the default behavior is to show the user the error message. This happens even if you have custom errors enabled for your application. Part of the reason that the default behavior is not to fol-low redirects is the potential to navigate away from a page where a user has invested time updating a state in stages without navigating between pages. Your objective is to keep the user’s data on his page while showing him an error. When redirected to an error page, the user’s reaction is to hit the Back but-ton. This can put the browser in a state before it begins interacting with the page, thereby losing the user’s data. The ASP.NET team has released prototypes of methods for putting interim stages in the browser’s history, but these have not been incorporated into an ASP.NET AJAX release yet.

When an error occurs in this condition, you don’t want the user to be redirected to a custom error page. Instead, you can provide an endRequest event handler and alert the user to the error without redirecting to an error page. Then you can return the user to the view they had before the error occurred to continue working with the application. The following JavaScript code demonstrates binding to the

function pageLoad() {
  Sys.WebForms.PageRequestManager.getInstance().add_endRequest("endRequestHandler");
}

function endRequestHandler(sender, arg) {
    if (arg.get_error() !== null) {
        //alert user of problem and continue
        arg.set_errorHandled(true);
    }
}

If your application doesn’t need to recover from error conditions in this way, you can set the allowCustomErrorsRedirect property of the ScriptManager to true to continue following the custom error handling that would occur when not using partial page rendering.

<asp:ScriptManager runat="server" ID="ScriptManager1"
AllowCustomErrorsRedirect="true" >

Handling Errors during Asynchronous Postback

ASP.NET AJAX allows you to trap an error that occurs during asynchronous postback processing. You can provide a static error message so that the user doesn’t have to see a potentially confusing .NET exception, or you can provide an error handler that will run on the server and do something more to recover from the error condition.

The ScriptManager has an AsyncPostBackErrorMessage property that can be used instead of the actual exception message that would normally be displayed.

<asp:ScriptManager runat="server" ID="ScriptManager1"
AsyncPostBackErrorMessage="Please try again tomorrow." >

The following code shows binding an event handler to the error event before it is sent to the client. It doesn’t simulate an error but shows how to set up your own code to run when an error occurs. The exact nature of how to recover gracefully is up to you, although I should admit that it is not always feasible to do so. Sometimes, trying to recover from a problem puts the user in a position of repeating the action even though server resources are not available to process the request. Staging an infinite loop for the user is not an optimal application experience. Just remember to spend a reasonable amount of time in testing error conditions to minimize the chance of unexpected behavior that may upset users.

<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<script runat="server">
private void OnAsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e) {
    ScriptManager.AsyncPostBackErrorMessage = "Please call me at home. Something
erroneous has occurred.";
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head id="Head1" runat="server">
  <title>Error Handling</title>
</head>
<body>
  <form id="Form1" runat="server">
  <asp:ScriptManager runat="server" ID="ScriptManager"
EnablePartialRendering="true" OnAsyncPostBackError="OnAsyncPostBackError">
  </asp:ScriptManager>
  </form>
</body>
</html>

Asynchronous Timeout

The ScriptManager allows you to customize how long to wait in the browser before considering the request orphaned and timing out. By default, the client will wait for the server to close the connection before assuming that the request has been abandoned. To specify a new default timeout for all requests, you use the AsyncPostBackTimeout property. The unit of measurement is milliseconds.

<asp:ScriptManager runat="server" ID="ScriptManager" EnablePartialRendering="true"
AsyncPostBackTimeout="3000" >


Customizing the Location of Scripts

You’ve seen how you can provide Release and Debug versions of JavaScript files on disk, and the ScriptManager will automatically reference the correct version. And you’ve seen how to embed scripts in a dll for easier servicing. ASP.NET AJAX also allows you to further customize script reference behavior.

Setting the Script Path

You may want to put the common scripts for nested applications in a single location to allow the browser to cache a single copy for multiple directories. Typically, ASP.NET AJAX returns relative path references for script, so the browser can end up caching multiple copies of a script for different directories or applications within the same domain. The ScriptManager’s ScriptPath property allows you to define a path to use in lieu of the normal discovery process. This allows you to put the scripts in a single location and have all references in the domain refer to that location.

At first, this may not seem like much, but when you consider that, even compressed, the amount of script to enable ASP.NET AJAX scenarios is more than 10K, allowing each user to get just one copy instead of one for each subdomain or nested application can really add up. The perceived performance for the user in eliminating excess calls can be significant.

The following code demonstrates setting the ScriptPath to a central location for greater caching. Of course, this will not bypass browser security restrictions regarding isolating domain references, but for nested directories, it can increase perceived performance significantly.

<form id="Form1" runat="server">
<asp:ScriptManager runat="server" ID="ScriptManager" EnablePartialRendering="true"
ScriptPath="~/CommonScripts" >
</asp:ScriptManager>
</form>

I didn’t say overall performance is better, but perceived performance can be improved. Here’s the explanation: There are two aspects to the user experience associated with loading script. The first is how long it takes to retrieve the script from the server. The browser is good about caching script, so I tend to discount this overhead. Once the browser has the script for an application, it doesn’t need to retrieve it for each subsequent request. The second aspect of performance is how long it takes to load and process the script. The ASP.NET team has done comparisons of the prototype and closure models for JavaScript development. The prototype model appears to yield the best overall performance across various browsers. Although the processing time can ultimately be the bottleneck in loading script, avoiding unnecessary script retrieval can help an application feel more responsive for the user.

Resolving Script References

In addition to setting a static location for scripts, you can provide a handler to determine the location of scripts to use at runtime. The ScriptManager has an event called ResolveScriptReference that will trigger for each script and allow you to customize the behavior. For many situations, using the ResolveScriptReference event may not be necessary, but when you find that something about a script needs to be modified without replacing the dll in which it resides, this event can be a lifesaver.

ScriptManager Proxy

All of the examples in this chapter have been free-standing pages. There hasn’t been a master page involved in the layout. But, of course, master pages are often the centerpiece of real-world site designs. To use ASP.NET partial rendering features, you need a ScriptManager control on the page, but there can be only one. So, if you have a Master page where there is a ScriptManager control, and you want to override or customize some settings in a content page, you cannot simply add another ScriptManager. Instead, ASP.NET provides the ScriptManagerProxy control.

The ScriptManagerProxy control allows a content page to interact with the primary ScriptManager control from the Master page and to set values unique to the content page being requested. The ScriptManagerProxy supports all of the same settings you have seen for the ScriptManager, except that it is used in a content page to reference the ScriptManager control that exists on the Master page. Using the proxy without a ScriptManager control to reference in the Master will result in an error.

Script Compression

One of the concerns about leveraging JavaScript in the browser more for AJAX scenarios is the increased volume of script being sent to the client. The ScriptManager compresses scripts so that the data sent from server to browser is smaller. In addition, the ASP.NET AJAX scripts are distinctly different in the release and debug versions. Besides just skipping the extra parameter validation and error information, the release version of the scripts employs a process for removing extraneous comments and unnecessary whitespace. Tabs, newlines, and extra spaces are removed to make the size of the script as small as possible before it is compressed and sent to the client. In fact, the scripts are analyzed to reduce variable names to single letters for internal use to minimize the overall script size.


Compatibility

Those who have written custom controls for ASP.NET 2.0 may look at the ScriptManager and wonder why many of the APIs are the same as what already exists on the ClientScriptManager object. The ASP.NET 2.0 ClientScriptManager was written to support postbacks and callbacks where the burden of partial page updates was left to the control developer. The ScriptManager focuses on enabling partial page rendering for controls natively. To allow the ScriptManager to effectively update parts of the page, custom controls need to use its methods for registering script so that partial page updates will correctly manage the script with the browser.

Controls can typically be updated to work with partial rendering by simply exchanging the calls to the ClientScriptManager for calls to the new ScriptManager control. For example, the ASP.NET validator controls registered JavaScript with the ClientScriptManager and would fail to execute correctly during partial page updates but work correctly after being updated to call the equivalent ScriptManager controls.


Summary

The ScriptManager is the centerpiece of ASP.NET AJAX functionality. It controls the switching between release and debug versions of scripts. You saw how to create embedded script resources and register them with the ScriptManager to get automatic switching between versions.

The ScriptManager lets you specify web service references and produces JavaScript proxies for easily calling them from the browser. JSON serialization is used to efficiently move data between client and server without some of the overhead associated with XML formatting.

The ScriptManager also provides access to the Authentication and Profile features of ASP.NET. This allows you more control over when and how you secure your application and leverage personalization data.

To participate in partial rendering with the UpdatePanel, controls need to use the script registration APIs on the ScriptManager. The ScriptManager can then control when and how the script is available in the browser to allow partial updates to happen and avoid reloading script unnecessarily.


Chapter 6: ASP.NET AJAX Networking

At the heart of AJAX development is the ability to make asynchronous web service calls from JavaScript code. For several years now, the major web browsers have included an XMLHttpRequest object for making HTTP requests. The XMLHttpRequest object is used to perform out-of-band communications with the server for invoking web services, executing callbacks, and performing partial page updates. This object allows the browser to send requests to the server and to receive data from the server without requiring the entire page to be posted back to the server and subsequently refreshed in the browser.

ASP.NET AJAX provides classes for managing web requests, processing responses, and detecting errors. It also provides support for serializing objects formatted as JSON (JavaScript Object Notation), which makes them readily usable in JavaScript in the browser. JSON is a standard serial format that is more lightweight than XML. In this chapter, you will learn how to use the ASP.NET JavaScript classes to perform networking tasks such as making synchronous and asynchronous HTTP requests, serializing .NET objects to and from JavaScript, and handling errors when web requests do not execute as expected.

The XMLHttpRequest Object

The XmlHttpRequest object was introduced by Microsoft as an ActiveX control for Internet Explorer 5. It was later added to Mozilla 1.0 and Safari 1.2. Now, all serious browsers include support for this object. In Internet Explorer, you pass the name of the object to the ActiveXObject function:

var request = new ActiveXObject("Microsoft.XMLHTTP");

In Firefox and most other browsers, you can simply use the new keyword to create an object directly in JavaScript. In these browsers, the XMLHttpRequest has been added natively to the DOM.

var request = new XMLHttpRequest();

The separate implementations have some common methods, and the behavior is much the same, but ASP.NET AJAX provides objects that give you an abstraction from the differences so you don’t have to worry about browser variations. The ASP.NET AJAX Extensions JavaScript objects are also similar to those available in the .NET Framework, so the object model is more familiar to .NET developers than the native XMLHttpRequest objects.

Because ASP.NET AJAX gives you a set of standard classes to ensure consistent behavior when accessing web resources, rather than using the browser-specific XMLHttpRequest object directly, I won’t go into the details of the XMLHttpRequest object except to point out some security and architectural issues.

XMLHttpRequest Security

The browsers all enforce a common security policy that restricts the XMLHttpRequest object from issuing cross-domain requests. This is a common-sense restriction that prevents a page from possibly sending sensitive data to a third-party site you aren’t specifically addressing. Your JavaScript code can only successfully issue requests against the domain in which the page originated. In fact, the policy prohibits requests that go to a different host, use a different protocol (like HTTP instead of HTTPS), or access the server on a different port. This limits your ability to write so-called mashup applications (the term mashup means to consolidate data from different sources) that leverage a variety of different sources on the web for a synergistic application. For example, JavaScript in a page request to http://www.wrox .com cannot use the XMLHttpRequest object to make calls to http://www.wiley.com (different domain) or to https://www.wrox.com (different protocol) or to http://www.wrox.com:81 (different port) or to http://scripts.wrox.com (different host). You are limited to using the XMLHttpRequest object only for issuing requests against the same domain, host, port, and protocol of the originating page.

Because there are many valid scenarios where you might want to allow mashups from different sites, you can take advantage of some tricks for accessing remote resources. The security restriction does not guard against including IFrame elements in a page that accesses content on different domains, because an IFrame is allowed to pull in a different page, possibly from a different site. You can then use the DOM to relay information to and from JavaScript running in that IFrame. Also, a web service on the originating site can act as a proxy and issue requests against a remote domain on behalf of your web page. The service then returns the remote content to the browser. This involves routing additional requests to your own server, when you would prefer going directly to the servers in another domain, but it satisfies the security constraints of the browser.

Object Properties and Methods

Although there are different implementations of the XMLHttpRequest object in the standard browsers, they share some common APIs. In ASP.NET AJAX applications, you can program against the WebRequest and WebRequestManager objects to simplify your interactions with the XMLHttpRequest object, but it is also helpful to understand what is being done in the underlying object.

Some common methods allow you to initiate a request using send() and to terminate a request by calling abort(). There are also methods for setting and retrieving HTTP headers by calling setRequestHeader and getResponseHeader. The status and statusText properties yield information about the results of the call. And there are properties for retrieving the body of the response as text (responseText) or in an XML format (responseXML).

Listing 6-1 (Time.aspx) is a web page that returns the server time to its caller. It takes no parameters and returns a string. Although it’s easy to handle strings, ASP.NET makes development easier by serial-izing and deserializing more complex types that can be passed between web services and JavaScript. You will see examples of this later in this chapter when web services are used instead of invoking an ASP.NET page directly.

Listing 6-1
Image from book

<%@ Page Language="C#" %>
<script runat="server">
protected override void OnLoad(EventArgs e) {
    base.OnLoad(e);
    Response.Write(DateTime.Now.ToUniversalTime());
}
</script>
Image from book

Listing 6-2 (CallTime.aspx) shows basic usage of the XMLHttpRequest object to call the time web page. This is the kind of classic JavaScript code you’d find in any generic AJAX web page. You can quickly see from this code that certain pieces of JavaScript would be repeated wherever you use XMLHttpRequest. ASP.NET AJAX abstracts much of this repetitive code to simplify and streamline your code. In this example, you first determine whether or not the user is browsing with Internet Explorer by checking for ActiveX support. If not, the native XMLHttpRequest object is used. Next, call the open method with parameters to perform an HTTP GET request, the service address, and a Boolean true to indicate that the request should be asynchronous. The readyStateChangedHandler function is assigned to the readyStateChange event, and then the send method is called.

Listing 6-2
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Networking</title>
<script type="text/javascript">

var xmlhttp;

function pageLoad() {
    if(window.ActiveXObject) {
        xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    }
    else {
        xmlhttp = new XMLHttpRequest();
    }
    xmlhttp.open("GET", "Time.aspx", true);
    xmlhttp.onreadystatechange = readyStateChangedHandler;
    xmlhttp.send();
}

function readyStateChangedHandler() {
    if(xmlhttp.readyState == 4) {
        alert(xmlhttp.responseText);
    }
}
</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    </asp:ScriptManager>
    <div>

    </div>
    </form>
</body>
</html>
Image from book

The readyStateChange event is fired each time the state of the XMLHttpRequest object transitions to a different phase of handling the request. The states are nonintuitively assigned fixed numbers, and 4 happens to be the state that indicates that the request has completed. Upon finding a status of 4, you display the time in an alert dialog. The code is not overly complex but does not include any error checking or do anything with the other states of the XMLHttpRequest object. As the use of the XMLHttpRequest object increases in an application, so does the complexity of the code, and the repetition of code can lead to errors and problems with maintainability. The ASP.NET AJAX WebRequest class makes leveraging the XMLHttpRequest object easier.


Making WebRequest Calls

The WebRequest class is the central object used for managing HTTP requests in ASP.NET AJAX. It provides a cross-browser compatible object for using the different underlying XMLHttpRequest implementations. Listing 6-3 (CallTimeWebRequest.aspx) is an .aspx page that does the equivalent of the request and response processing shown in Listing 6-2.

Listing 6-3
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Networking</title>
<script type="text/javascript">

var xmlhttp;

function pageLoad() {
    var webRequest = new Sys.Net.WebRequest();
    webRequest.set_url("Time.aspx");
    webRequest.add_completed(completedHandler);
    webRequest.invoke();
}

function completedHandler(result, eventArgs) {
    if(result.get_responseAvailable()) {
        alert(result.get_statusText());
        alert(result.get_responseData());
    }
}
</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    </asp:ScriptManager>
    <div>
    </div>
    </form>
</body>
</html>
Image from book

First, note that the code creates a Sys.Net.WebRequest object directly. You do not have to bother with checking the browser compatibility. You set the service address, add a completed callback function, and invoke the request. In the callback function, there is no need to check different integers against the status codes they represent. Instead, there is a check that the response is available, coupled with access to the data. This isn’t dramatically different from what is done in Listing 6-2 when using the XMLHttpRequest object directly; however, it is more straightforward and avoids your having to look at each and every state change in the underlying object.

The WebRequest object provides abstractions for much of what is typically handled by the browser. Many of the interactions between browser and server can be controlled through the WebRequest object. The HTTP protocol defines a set of headers that you may want to control. You also may find that you don’t want to accept the default behavior of waiting indefinitely for a server to respond once you have initiated a request.

Setting the HTTP Verb

HTTP requests include a verb for processing. Most of the time, you use either GET to query the server or POST to upload data. The HEAD verb is also used to gather information about the content type and to get caching information. The WebRequest object assumes a GET request unless you specify otherwise. A GET passes values in the query string, which seems as though it would be a bad idea from a security standpoint. In this case, however, the user won’t see the query string anyway. A POST is more appropriate if you have a lot of data to send to the server.

The WebRequest provides functions to retrieve and set the HTTP verb. In Debug mode, the class will check for a nonzero length verb, but it has no way to verify that the verb is supported by the server, so any nonzero length value is accepted.

Listing 6-4 shows the EchoABC.aspx page that will receive and process a POST from the code in Listing 6-5. Note that it looks for a value by name in the request’s form collection, since a POST doesn’t transfer data in the query string.

Listing 6-4
Image from book

<%@ Page Language="C#" %>
<script runat="server">
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    if(Request.Form["abc"] != null) {
        Response.Write(Server.HtmlEncode(Request.Form["abc"]));
    }
}
</script>
Image from book

The page in Listing 6-5 (SetVerb.aspx) sets the HTTP verb to POST and sets the body to a name-value pair. The key is checked in the form collection from Listing 6-4, and its result is returned.

Listing 6-5
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Networking</title>
<script type="text/javascript">
function pageLoad() {
    var webRequest = new Sys.Net.WebRequest();
    webRequest.set_url('EchoABC.aspx');
    webRequest.set_httpVerb('POST');
    webRequest.set_body('abc=123');
    webRequest.add_completed(completedHandler);
    webRequest.invoke();
}

function completedHandler(result, eventArgs) {
    if(result.get_responseAvailable()) {
        alert(result.get_statusText());
        alert(result.get_responseData());
    }
}
</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    </asp:ScriptManager>
    <div>
    </div>
    </form>
</body>
</html>
Image from book

Establishing Timeout Limits

A call to set_timeout on the WebRequest can impose a limit on how long to you will wait for the response. The result can then be checked to find out if the call was terminated before reaching the limit. The get_timedOut function returns true if the call did not complete in time. To ensure that the limit is reached, you put the thread to sleep for 10 seconds in the page being requested. This lets you test what happens in the client page when the server takes too long to respond.

System.Threading.Thread.Sleep(10000);

Listing 6-6 (Timeout.aspx) adds a call to set_timeout for five seconds and then discovers the timeout problem in the completion callback.

Listing 6-6
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Networking</title>
<script type="text/javascript">
function pageLoad() {
    var webRequest = new Sys.Net.WebRequest();
    webRequest.set_url('Sleep.aspx');
    webRequest.set_httpVerb('POST');
    webRequest.set_body('abc=123');
    webRequest.set_timeout(5000);
    webRequest.add_completed(completedHandler);
    webRequest.invoke();
}

function completedHandler(result, eventArgs) {

    if(result.get_timedOut()) {
        alert('timed out');
    }
    if(result.get_responseAvailable()) {
        alert(result.get_statusText());
        alert(result.get_responseData());
    }
}
</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    </asp:ScriptManager>
    <div>
    Making a WebRequest.
    </div>
    </form>
</body>
</html>
Image from book

Adding Custom Headers

In addition to being able to detect timeouts, control the verb, and set the contents, you can send custom HTTP headers along with the request. Listing 6-7 is the EchoHeaders.aspx page that simply echoes the HTTP headers included in a request. This server-side code creates a return page that shows exactly what is passed to it in the headers.

Listing 6-7
Image from book

<%@ Page Language="C#" %>
<script runat="server">
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    NameValueCollection headers = Request.Headers;
    foreach (string key in headers.Keys)
    {
        Response.Write(Server.HtmlEncode(key) +
            " = " +
            Server.HtmlEncode(headers[key]) +
            "<br />\r\n");
    }
}
</script>
Image from book

Requests to the page from Internet Explorer and Firefox are shown in Figure 6-1.

Image from book
Figure 6-1

The headers sent by the different browsers are not the same. Internet Explorer provides the versions of the .NET Framework that it supports in the headers, while Firefox does not. Both browsers accept gzip and deflate for encodings. The language header differs based on the selections made in the different browsers. (I don’t speak Japanese, but I have it in my list of preferred languages of both browsers for testing localization features.)

Listing 6-8 (CustomHeaders.aspx) makes a call to the EchoHeaders.aspx page while adding a few unique headers to the request.

Listing 6-8
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Networking</title>
<script type="text/javascript">
function pageLoad() {
    var webRequest = new Sys.Net.WebRequest();
    webRequest.set_url('EchoHeaders.
    webRequest.get_headers()["Preferred-Publisher"] = "Wrox";
    webRequest.get_headers()["Preferred-Album"] = "Rift";
    webRequest.get_headers()["UA-CPU"] = "Altair MIPS";
    webRequest.add_completed(completedHandler);
    webRequest.invoke();
}

function completedHandler(result, eventArgs) {
    if(result.get_responseAvailable()) {
        $get("placeholder").innerHTML = result.get_responseData();
    }
}
</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    </asp:ScriptManager>
    <div id="placeholder">
    </div>
    </form>
</body>
</html>
Image from book

The same requests with some additional headers from Internet Explorer and Firefox now show different results than the previous request. The Preferred-Publisher and Preferred-Album headers are now reported as part of the request. Although the preferred-publisher and preferred-album are displayed, in the test with IE, the UA-CPU header is the same as it was before the request that tried to modify it. This is because IE set this header after the page set its value, since this is a standard header that IE always sets itself. The WebRequest object allows you to add headers, but the browser itself still has the last say about what is actually transmitted to the server. Figure 6-2 shows the result of an additional set of requests from Internet Explorer and Firefox with the requests that include new additional headers.

Passing Extra Data

The WebRequest class will automatically pass data from the caller to the completion event handler function. Many times, the callback code can benefit from having more than is typically provided in normal operations. The ASP.NET AJAX userContext parameter provides a way to pass data from the caller to the callback without even sending it to the server, and this tells the event handler some extra context information about why or where the call was made. The Framework manages this extra information and includes it as part of the callback parameters automatically, so it looks as though this information came from the server. Listing 6-9 (UserContext.aspx) is a page that adds userContext data to the call to the EchoHeaders page.

Image from book
Figure 6-2
Listing 6-9
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Networking</title>
<script type="text/javascript">
function pageLoad() {
    var webRequest = new Sys.Net.WebRequest();
    webRequest.set_url('EchoHeaders.aspx');
    webRequest.set_userContext('send this data to the callback');
    webRequest.add_completed(completedHandler);
    webRequest.invoke();
}

function completedHandler(result, eventArgs) {
    if(result.get_responseAvailable()) {

        var userContext = result.get_webRequest().get_userContext();
        $get('placeholder')
    }
}
</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    </asp:ScriptManager>
    <div id="placeholder" >
    </div>
    </form>
</body>
</html>
Image from book

The WebRequest does not include the user context information in what is sent to the server. Instead, the data is stored locally and then made available during the callback. You can store complex JavaScript types without needing to worry about how they will be serialized for including in an HTTP request. This also keeps the request/response payload lighter.

Resolving the Full URL

In web development, it is often useful to make decisions based on what part of an application the user is accessing. The WebRequest provides a method for retrieving the full URL of a request that is about to be made. When setting up the request, you can specify a complete address for the URL or a relative path. The getResolvedUrl function will always yield the full path regardless of which form you provide:

alert(webRequest.getResolvedUrl());

It’s important to note that you should not drive security decisions based on the resolved URL in JavaScript running in the browser. The base URL can be spoofed through script running in the JavaScript console. It’s safe to assume that any JavaScript in your page might be modified by some kind of Trojan program or unauthorized process on the user’s machine, so you must take care in how much trust you place in that code. You should rely on server-side validation of every aspect in the client/server interaction in cases where you have security concerns.


Managing Web Requests

The WebRequest object works with a static WebRequestManager JavaScript object to coordinate the behavior of the ASP.NET AJAX networking stack. Even though you may not reference the object directly, the individual WebRequest objects that you create work with it to execute requests using the XMLHttpRequest object. In fact, the WebRequestManager does not know the specifics of the XMLHttpRequest object. Instead, it works with the WebRequestExecutor, an object which abstracts some of the underlying browser-specific details. These JavaScript objects are part of the Microsoft AJAX Library. Later in this chapter, you will learn how to use a WebRequestExecutor tailored for working with XML data instead of JSON.

Default Timeout

Listing 6-10 (TimedSleep.aspx) modifies the Sleep.aspx page to return the amount of time that has passed during request processing instead of specific form variables. The sleep of two seconds simulates a server-side process that may require two seconds to execute.

Listing 6-10
Image from book

<%@ Page Language="C#" %>
<script runat="server">
protected override void OnLoad(EventArgs
{
    base.OnLoad(e);
    DateTime start = DateTime.Now;
    System.Threading.Thread.Sleep(2000);
    DateTime finish = DateTime.Now;
    Response.Write(finish - start);
}
</script>
Image from book

Listing 6-11 (CallTimedSleep.aspx) does not specify a timeout, so it gets the default value of 0, which indicates that the WebRequest should be allowed to wait indefinitely for a response from the server.

Listing 6-11
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Networking</title>
<script type="text/javascript">
var numberOfTries = 0;

function pageLoad() {
    var webRequest = new Sys.Net.WebRequest();
    webRequest.set_url('TimedSleep.aspx');
    webRequest.add_completed(completedHandler);
    webRequest.invoke();

}

function completedHandler(result, eventArgs) {
    if(result.get_responseAvailable()) {
        $get('placeholder').innerText = "time taken = " +
result.get_responseData();
    }
}
</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    </asp:ScriptManager>
    <div id="placeholder">processing request</div><br />
    </form>
</body>
</html>
Image from book

The result of requesting the page using Internet Explorer and Firefox is shown in Figure 6-3.

Image from book
Figure 6-3

Instead of explicitly setting the timeout on each WebRequest object created in the page, the default timeout for all web requests can be set on the WebRequestManager. Listing 6-12 (SetDefaultTimeout.aspx) shows the modified pageLoad and completion handler code to demonstrate this. The default timeout is set to one second, but the page is going to sleep for two, so it will always fail. In the completion event, the timeout condition is detected and a new request is created. It will also have the one-second timeout given to the WebRequestManager.

Listing 6-12
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Networking</title>
<script type="text/javascript">
var numberOfTries = 0;

function pageLoad() {

    Sys.Net.WebRequestManager.set_defaultTimeout(1000);
    var webRequest = new Sys.Net.WebRequest();
    webRequest.set_url('TimedSleep.aspx');
    webRequest.add_completed(completedHandler);
    webRequest.invoke();

}

function completedHandler(result, eventArgs) {
    if(result.get_timedOut()) {
        $get('placeholder').innerText = "Timeout. Trying again after " +
numberOfTries + " times";
        numberOfTries = numberOfTries + 1;
        var anotherWebRequest = new Sys.Net.WebRequest();
        anotherWebRequest.set_url('TimedSleep.aspx');
        anotherWebRequest.add_completed(completedHandler);
        anotherWebRequest.invoke();
    }

    if(result.get_responseAvailable()) {
        $get('placeholder').innerHTML = "time taken = " +
result.get_responseData();
    }
}
</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    </asp:ScriptManager>
    <div id="placeholder">processing request</div><br />
    </form>
</body>
</html>
Image from book

Global Web Request Handling

Just as you may want to set a timeout to apply to all web requests, you may also want to provide a single function as the completion handler for all requests. In previous examples, the add_completed method was called on the individual WebRequest, but you can see that this would become repetitious when defining a set of request objects that all use the same target.

The WebRequestManager has the add_completedRequest and remove_completedRequest methods for attaching and detaching event handlers to be used for all requests. Listing 6-13 (GlobalCompleted.aspx)is a page that calls the time.aspx and TimedSleep.aspx pages and uses a single handler for both WebRequest instances.

Listing 6-13
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Networking</title>
<script type="text/javascript">

function pageLoad() {
    Sys.Net.WebRequestManager.add_completedRequest(completedHandler);

    var webRequest = new Sys.Net.WebRequest();
    webRequest.set_url('time.aspx');

    var webRequest2 = new Sys.Net.WebRequest();
    webRequest2.set_url('TimedSleep.aspx');

    Sys.Net.WebRequestManager.executeRequest(webRequest2);
    Sys.Net.WebRequestManager.executeRequest(webRequest);

}

function completedHandler(result) {
    if(result.get_responseAvailable()) {
        $get('placeholder').innerHTML += "<br />"
            result.get_webRequest().get_url() +
            " returned " +
            result.get_responseData ();
    }
}

</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    </asp:ScriptManager>
    <div id="placeholder">processing request</div><br />
    </form>
</body>
</html>
Image from book

Because this code just displays the results of the call, it has a single completion handler, registered with the WebRequestManager. When you call or invoke a WebRequest, it ultimately gets processed by the WebRequestManager’s executeRequest method. You can also call the executeRequest method directly, as shown in Listing 6-13.

Along with the data returned from executing the web request, the URL of the page is also displayed. The completion handler receives an argument of the type NetworkRequestEventArgs, which has a get_webRequest method. You then have access to the original WebRequest object.

The WebRequestManager also allows you to register event handlers to be called for each request as it is about to be called:

Sys.Net.WebRequestManager.add_invokingRequest(invokingHandler);

The CancelEventArgs passed to the invokingRequest handler allow you to cancel the request before it is actually executed.

function invokingHandler(sender, eventArgs) {
    if(eventArgs.get_webRequest().get_url() === 'TimedSleep.aspx')
        eventArgs.set_cancel(true);
    }
}

You can attach multiple event handlers for the invoking and completed events, and they will all be called. Even if one of the invoking event handlers cancels the request, the rest of the invoking handlers will still be called, so you have to be careful. And if you attach a completion handler directly to a WebRequest in addition to using the WebRequestManager, they will both be called.

The WebRequestExecutor

The WebRequestManager works with WebRequest objects to process requests of using an executor object. You saw examples of using this to issue requests for .aspx pages and .asmx web services that are only returning text. But a lot of message passing on the web utilizes XML data for increased fidelity, flexibility, and portability. The default executor used by the WebRequestManager is XMLHttpExecutor. The WebRequestManager includes functions to get and set which default executor you want it to use:

Sys.Net.WebRequestManager.get_defaultExecutorType();
Sys.Net.WebRequestManager.set_defaultExecutorType(Sys.Net.XMLHttpExecutor);

In addition to the WebRequestExecutor, ASP.NET AJAX includes an XMLHttpExecutor that will parse the XML results of a request for you. To demonstrate this process, Listing 6-14 (XMLTime.aspx) is a modification of the earlier page that returns the time from the server, but now the result is packaged as XML.

Listing 6-14
Image from book

<%@ Page Language="C#" %>
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        Response.Write("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
        Response.Write("<time><universalTime>" +
            DateTime.Now.ToUniversalTime() +
            "</universalTime></time>");
    }

</script>
Image from book

When the XMLHttpExecutor is used, the results will be parsed as XML and the XML will be available for use in the DOM. The default executor type is the XMLHttpExecutor, but I am creating it explicitly in Listing 6-15 (XMLHttpExecutor.aspx) to show that you can call the executeRequest method on it directly.

Listing 6-15
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Networking</title>
<script type="text/javascript">
function pageLoad() {
    Sys.Net.WebRequestManager.add_completedRequest(completedHandler);
    var webRequest = new Sys.Net.WebRequest();
    webRequest.set_url('XMLTime.aspx');
    var executor = new Sys.Net.XMLHttpExecutor();
    webRequest.set_executor(executor);
    executor.executeRequest();
}

function completedHandler(result) {
    if(result.get_responseAvailable()) {
        alert(result.get_xml().xml);
    }
}

</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    </asp:ScriptManager>
    </form>
</body>
</html>
Image from book


Passing Data

The X in AJAX is for XML, and indeed many web services today use XML for sending and receiving data. XML parsers are readily available in the browser, and classes for creating, manipulating, and serial-izing XML data on the server are plentiful. XML has also been the foundation for the creation of applications that leverage service-oriented architectures.

As developers have recognized the potential for passing data between client and server using XML, they have also recognized that XML is not always the ideal format for working with data. This section discusses JavaScript Object Notation (JSON), which is a JavaScript-specific method of serializing data that has less overhead than XML.

Serialization

Serialization is the process of encoding data in a way that allows it to be sent over a serial connection, such as an HTTP channel, that will allow it to be understood on the other side of the connection and can then be deserialized back into its original form. This is commonly used with structured types, since simple types such as strings are inherently compatible with serial channels. When objects are held in memory by a single application, there isn’t a need to serialize them. It doesn’t really matter how the computer represents them in memory as long as the fields, properties, and methods behave as expected. But when objects need to cross process or machine boundaries, serialization becomes essential in order for that object to be faithfully reconstructed at the other end.

The format used to serialize an object must be agreed upon by both parties that are exchanging it. If the format is not understood by both sides, the object will be corrupted after being transferred since the deserialization routine won’t know how to re-create the object. For example, if I write a song, record it, and give you the file as an MP3, chances are you will have access to a device or program that can play it. My MP3 file is a serialization of the song. I could decide that I would record each instrument separately and encode it that way in the file so that I could easily modify a single instrument in a song. If I gave you the file in this custom format, you wouldn’t be able to play it unless I also provided you with software that understood the serialization format I had used.

When an object is to be shared, the receiver needs to understand how the sender will format the object for transmission. There are binary formats for serialization that can’t be understood when looking at the data directly. And there are formats like XML and JSON, where you can read the serialized object and make sense of it with only a little background on these formats. You don’t have to know specifically how every object will be encoded as XML or JSON, because the rules of these formats are well established and will easily apply to many different kinds of objects.

XML and JSON are most commonly used for exchanging data, not for exchanging programmable objects directly. In other words, functions or methods are not usually passed over a wire. You may be familiar with the remoting serialization in .NET that allows objects, including their methods and properties, to be shared across process and machine boundaries, and although you could devise a way to use JSON or XML formats to do this, it would be an atypical use of these formats.

JSON Format

The JSON format leverages a subset of the object literal notation that JavaScript supports natively. In general, the information at www.json.org classifies the notation of objects as being either unordered key-value pairs, or ordered lists of items.

Unordered key-value pairs are separated by colons and surrounded by curly braces. Ordered lists, or arrays, are separated with commas and surrounded by right and left brackets. Individual items or objects are also separated by commas. Look at the following object:

{
    "artist" : "Phish",
    "title" : "A Picture of Nectar",
    "releaseYear" : 1992,
    "tracks" : [
        "Llama",
        "Eliza",
        "Cavern",
        "Poor Heart",
        "Stash",
        "Manteca",
        "Guelah Papyrus",
        "Magilla",
        "The Landlady",
        "Glide",
        "Tweezer",
        "The Mango Song",
        "Chalk Dust Torture",
        "Faht",
        "Catapult",
        "Tweezer Reprise"
    ]
}

This object represents an album. It has fields for the artist, title, year of release, and the array of song tracks. Individual elements can be strings, numbers, Booleans, or null. You can see that the JSON syntax is very easy to read and understand. For comparison, I have also represented the album in XML.

<?xml version="1.0" encoding="utf-8" ?>
<album>
  <artist>Phish</artist>
  <title>A Picture of Nectar</title>
  <releaseYear>1992</releaseYear>
  <tracks>
    <track>Llama</track>
    <track>Eliza</track>
    <track>Cavern</track>
    <track>Poor Heart</track>
    <track>Stash</track>
    <track>Manteca</track>
    <track>Guelah Papyrus</track>
    <track>Magilla</track>
    <track>The Landlady</track>
    <track>Glide</track>
    <track>Tweezer</track>
    <track>The Mango Song</track>
    <track>Chalk Dust Torture</track>
    <track>Faht</track>
    <track>Catapult</track>
    <track>Tweezer Reprise</track>
  </tracks>
</album>

The XML format is also readable and easy to understand but has a lot more overhead in terms of raw size than the equivalent JSON. Of course, you can also apply schema to XML for validation of types and apply XSLT transformations to XML more readily than with JSON, but for many applications, this additional flexibility is not needed.

Deserializing JSON-formatted types in the browser has one distinct advantage to working with XML - it is inexpensive and fast to execute. Remember that JSON leverages the object notation that is natively part of JavaScript, so accessing JSON-encoded data is as easy as using the eval operator on it and working with the type that it produces. The eval operator is a general constructor of objects encoded in JSON. Listing 6-16 (Eval.aspx) is a page that wraps the JSON string in parentheses and calls eval on it. The object returned has properties for the artist, title, year of release, and an array of the tracks.

Listing 6-16
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Networking</title>
<script type="text/javascript">
var JSONstring = '{' +
    '"artist" : "Phish",' +
    '"title" : "A Picture of Nectar",'
    '"releaseYear" : 1992,'
    '"tracks" : [' +
    '    "Llama",' +
    '    "Eliza",' +
    '    "Cavern",' +
    '    "Poor Heart",' +
    '    "Stash",' +
    '    "Manteca",' +
    '    "Guelah Papyrus",'
    '    "Magilla",' +
    '    "The Landlady",' +
    '    "Glide",' +
    '    "Tweezer",' +
    '    "The Mango Song",'
    '    "Chalk Dust Torture",'
    '    "Faht",' +
    '    "Catapult",' +
    '    "Tweezer Reprise"'
    ']' +
'}';

function pageLoad() {
    var album = eval("(" + JSONstring + ")");
    var innerHTML = "artist = " + album.artist + "<br />" +
        "title = " + album.title + "<br />" +
        "releaseYear = " + album.releaseYear;
    $get('placeholder').

    var tracks = "";
    for(var i = 0; i < album.tracks.length; i++) {
        tracks += "track #" + i + " = " + album.tracks[i] + "<br />";
    }
    $get('placeholder2').
}

</script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    </asp:ScriptManager>
    <div id='placeholder'></div>
    <div id='placeholder2'></div>
    </form>
</body>
</html>
Image from book

Figure 6-4 shows the identical output in Internet Explorer and Firefox. Because the code is just calling the JavaScript eval method on the JSON-formatted object, no special processing code is required for the different browsers.

Image from book
Figure 6-4

JSON Serialization

In the previous example, the JSON string was created explicitly to show the syntax and to demonstrate that calling eval on it results in an object. Most of the time, you aren’t creating the JSON strings in JavaScript code directly. Instead, you will want to create JavaScript objects that correspond to .NET objects created on the server. Thus, the server might create a small .NET entity class that gets serialized into JSON and later deserialized into a JavaScript object in the browser. Thankfully, ASP.NET AJAX makes working with JSON easy. Listing 6-17 (Album.asmx) is a web service that has the equivalent Album .NET class defined earlier in JavaScript. There are properties for title, artist, the year of release, and the album tracks.

Listing 6-17
Image from book

<%@ WebService Language="C#" Class="AlbumProxy" %>

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Script.Services;
using System.Net;
using System.Xml.Serialization;
using System.Web.Script.Serialization;

public class Album
{
    private string _artist = String.Empty;
    private string _title = String.Empty;
    private int _releaseYear;
    private string[] _tracks = new string[16];
    private DateTime _dateTime = DateTime.Now;
    private string _personalInfo = "do not show this";

    [ScriptIgnore()]
    public string PersonalInfo
    {
        get {
            return _personalInfo;
        }
        set {
            _personalInfo = value;
        }
    }

    public Album() {
    }

    public string Artist {
        get {
            return _artist;
        }
        set {
            _artist = value;
        }
    }

    public string Title {
        get {
            return _title;
        }
        set {
            _title = value;
        }
    }

    public int ReleaseYear {
        get {
            return _releaseYear;
        }
        set {
            _releaseYear = value;
        }
    }

    public string[] Tracks {
        get {
            return _tracks;
        }
        set {
            _tracks = value;
        }
    }
}

[ScriptService]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class AlbumProxy : System.Web.Services.WebService {

    private Album _album;

    public AlbumProxy() {
        _album = new Album();
        _album.Artist = "Phish";
        _album.Title = "A Picture of Nectar";
        _album.ReleaseYear = 1992;
        _album.Tracks.SetValue("Llama", 0);
        _album.Tracks.SetValue("Eliza", 1);
        _album.Tracks.SetValue("Cavern", 2);
        _album.Tracks.SetValue("Poor Heart", 3);
        _album.Tracks.SetValue("Stash", 4);
        _album.Tracks.SetValue("Manteca", 5);
        _album.Tracks.SetValue("Guelah Papyrus", 6);
        _album.Tracks.SetValue("Magilla", 7);
        _album.Tracks.SetValue("The Landlady", 8);
        _album.Tracks.SetValue("Glide", 9);
        _album.Tracks.SetValue("Tweezer", 10);
        _album.Tracks.SetValue("The Mango Song", 11);
        _album.Tracks.SetValue("Chalk Dust Torture", 12);
        _album.Tracks.SetValue("Faht", 13);
        _album.Tracks.SetValue("Catapult", 14);
        _album.Tracks.SetValue("Tweezer Reprise", 15);
    }

    [WebMethod]
    [XmlInclude(typeof(Album))]
    public object GetAlbum() {
        return _album;
    }

    [WebMethod]
    [XmlInclude(typeof(Album))]
    public object GetAlbumJSON()
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        return serializer.Serialize(_album);
    }
}
Image from book

The web service exposes two methods for retrieving an album instance. If you navigate your browser directly to the album.asmx URL, you will get options for invoking each of the methods as a SOAP service. The GetAlbum method returns the object directly. It is serialized using XML. The GetAlbumJSON method uses the JavaScriptSerializer class to return the album in the JSON format, but when requested as a SOAP service, the JSON is still packed as the value of the XML element, so you’ll see a SOAP XML wrapper around the JSON encoded object. The output from invoking both functions is shown in Figure 6-5. The XML output is shown on the left and the JSON output is shown on the right.

Image from book
Figure 6-5

You can see that the data is fundamentally the same but that the JSON string is more compact, even though it has a simple XML wrapper around it. Sometimes you want the additional flexibility you can get with XML, but for many services, the JSON format is sufficient and smaller. Besides the size advantage of JSON, its use avoids the lack of a standard XML parser being available across all modern browsers.

The properties of the .NET type are serialized into fields of the JavaScript object, as you would expect. Listing 6-18 (ScriptProxy.aspx) includes a ScriptReference to the album.asmx web service. The methods can then be invoked by the proxy objects that ASP.NET AJAX generates for you. The completion function that gets the JSON data can deserialize the result in two steps. First, it applies eval to the string after wrapping it in parentheses to get an album object. Then the deserialize function of the JavaScriptSerializer object is called. The completion function that gets the XML data just applies eval. The parentheses are needed when processing the first results but not the second. This is because ASP.NET AJAX manages the serialization of the return value. When it is called from the generated script proxy, ASP.NET doesn’t actually return the XML, but the JSON formatted object is returned instead.

Listing 6-18
Image from book

<%@Import Namespace="System.Web.Script.Serialization"  %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Networking</title>
<script type="text/javascript">

function pageLoad() {
    AlbumProxy.GetAlbumJSON(completionJSON);
    AlbumProxy.GetAlbum(completionXML);
}

function completionJSON(result) {
    var album = eval("(" + result + ")");
    $get('placeholder').innerHTML = album

    var album2 = Sys.Serialization.JavaScriptSerializer.deserialize(result);
    $get('placeholder2').innerHTML = album2.Artist;
}

function completionXML(result) {
    var album = eval(result);
    $get('placeholder3').innerHTML = album.ReleaseYear;
}

</script></head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1">
    <Services>
        <asp:ServiceReference Path="album.asmx" />
    </Services>
    </asp:ScriptManager>
    <div id='placeholder'></div><br
    <div id='placeholder2'></div><br
    <div id='placeholder3'></div>
    </form>
</body>
</html>
Image from book

PageMethods

You saw that a web service class could be decorated with the ScriptService attribute and ASP.NET AJAX would provide JavaScript proxies to invoke it from the browser and manage the types. You can also get proxies for invoking methods contained directly in the page. The method must be a static method. You must also set the EnablePageMethods property of the ScriptManager to true. Listing 6-19 (PageMethods.aspx) includes the method for returning the time on the server directly in the page. The method is marked with both the WebMethod and ScriptMethod attributes. The ScriptMethod attribute is optional. Here it is used to change from the default POST to the GET HTTP verb. The method defined on the server is now available as a member of the PageMethods object on the client. On the client it is a proxy for invoking the method on the server. If it took parameters, they would be the first argument to the JavaScript method, followed by the completion callback, error callback, and user context data.

Listing 6-19
Image from book

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web.Services" %>
<%@ Import Namespace="System.Web.Script.Services" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    [WebMethod]
    [ScriptMethod(UseHttpGet=true)]
    public static string ServerTime() {
        return DateTime.Now.ToUniversalTime().ToString();
    }
</script>
<script type="text/javascript">
function pageLoad() {
    $addHandler($get('timeButton'),
}

function getTime() {
    PageMethods.ServerTime(OnServerTimeComplete);
}

function OnServerTimeComplete(result, userContext, methodName) {
    alert(result);
}

</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>ASP.NET AJAX PageMethod</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager"
EnablePageMethods="true"></asp:ScriptManager>
    <div>
    <input type="button" value="Show Server Time" id="timeButton" />
    </div>
    </form>
</body>
</html>
Image from book

Working with Dates

The JSON format does not natively support dates. It handles strings, numbers, Booleans, arrays, and nulls but lacks support for Date objects. If a .NET object contains a date, the JavaScriptSerializer class will encode it in a format that the AJAX script library knows how to deserialize in the browser. However, this means that you would now need to use the deserialize method of the Sys.Serialization.JavaScriptSerializer object instead of calling eval directly so that the date will be parsed for you:

var objectWithDate =
Sys.Serialization.JavaScriptSerializer.deserialize(JSONstring);
var date = new Date(objectWithDate.dateProperty);

Bypassing Serialization

Much of the appeal for using the JSON format is due to the streamlined size of encoded objects and the fast speed of working with these types in JavaScript. You have seen that ASP.NET AJAX will automatically turn instances of .NET objects into the JSON format so that they can be deserialized and used in the browser. But since part of the appeal is the reduced size of the JSON format, it follows that you want to be able to exert some control over the serialization behavior to reduce the number of properties you need to serialize. ASP.NET provides the ScriptIgnore attribute to exclude designated properties from being serialized.

Suppose I add a .NET property that I do not want sent to the client in the album object:

private string _personalInfo = "do not show this";

public string PersonalInfo
{
    get {
        return _personalInfo;
    }
    set {
        _personalInfo = value;
    }
}

The default behavior of the serializer is to include all of the properties available. When looking at the data returned by invoking the object, you see that this new property is included. This is shown in Figure 6-6.

Image from book
Figure 6-6

Adding the ScriptIgnore attribute to a property prevents the JavaScriptSerializer from including it in the serialized form of the object, which will limit the amount of data you send down to the browser:

[ScriptIgnore()]
public string PersonalInfo
{
    get {
        return _personalInfo;
    }

    set {
        _personalInfo = value;
    }
}

You could argue that the default should be to avoid serializing properties unless they are explicitly designated as being serializable for JSON, but this seemed counter to the expected use of creating simple types for passing from the server to client for manipulation in the browser. In other words, Microsoft opted for ease of use in the general case.

Configuring the JSON Serializer

The serialization examples you have seen here were pretty straightforward, but in real life, the objects you work with are sometimes not so trivial. You rarely care what the time on the server is, and business objects are not usually limited to two or three string properties.

The ASP.NET JavaScriptSerializer can be configured to control its behavior. The default is to limit the depth to which objects are traversed in serializing to one hundred. There is also a limit to the length of the JSON string that is produced when serializing. If the string is longer than 102,400 characters, an error is produced. These limits are in place to guard against inadvertently serializing very large data structures and transmitting them to the client where the ability of the script engine to work with them will be taxed. You probably won’t want to serialize an ADO.NET DataTable having half a million records!

<system.web.extensions>
  <scripting>
    <webServices>
    <jsonSerialization maxJsonLength="102400" recursionLimit="100">
    </jsonSerialization>
    </webServices>
  </scripting>
</system.web.extensions>

Custom Serialization

The ASP.NET AJAX JavaScriptSerializer supports serializing the primitive types that you have seen already in this chapter. But this brute force approach doesn’t scale well to complex types. In practice, you may find that the default serialization is not exactly what you are after. You can shape the server object into the structure that you want to work with in the browser using a custom serialization converter.

You create the custom converter by inheriting from the JavaScriptConverter type in the System.Web.Script.Serialization.Converters namespace. The JavaScriptConverter base type has three public abstract methods to override. The Deserialize method allows you to take in JSON and turn it back into an instance of the supported type. You can override the SupportedTypes method to return the IEnumerable collection of types that can be handled. And the Serialize method is expected to return an IDictionary collection of name-value pairs to include in the JSON output for the object.

You can configure custom converters in the converters element inside the jsonSerialization ele-ment. Here is an excerpt of the configuration from the ASP.NET “Preview” configuration that includes custom converters for the DataSet, DataRow, and DataTable.

    <system.web.extensions>
    <scripting>
      <webServices>
      <jsonSerialization maxJsonLength="500">
        <converters>
            <add name="DataSetConverter"
type="Microsoft.Web.Preview.Script.Serialization.Converters.DataSetConverter,
Microsoft.Web.Preview, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
            <add name="DataRowConverter"
type="Microsoft.Web.Preview.Script.Serialization.Converters.DataRowConverter,
Microsoft.Web.Preview, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
            <add name="DataTableConverter"
type="Microsoft.Web.Preview.Script.Serialization.Converters.DataTableConverter,
  Microsoft.Web.Preview, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
        </converters>
    </jsonSerialization>
    </webServices>
  </scripting>
</system.web.extensions>


Summary

The XMLHttpRequest is at the heart of ASP.NET AJAX development. This object is exposed as an ActiveX control in Internet Explorer and as part of the DOM in Firefox. The XMLHttpRequest object allows you to communicate with the server to do partial page updates and invoke web services.

Although the XMLHttpRequest object is limited to making calls back to the originating domain, proxy classes for retrieving the object and the use of embedded IFrames that talk to third party services allow you to build mashup applications that aggregate remote applications and service providers.

JSON object notation provides a lightweight human-readable format for serializing objects between server and client. ASP.NET AJAX makes use of the JSON format to provide easier interoperability between .NET on the server and JavaScript on the client. You can serialize between .NET objects and JavaScript objects using the System.Web.Script.Serialization.JavaScriptSerializer type on the server and the Sys.Serialization.JavaScriptSerializer object in the browser.

In addition to supporting seamless translation of compatible types between client and server, ASP.NET AJAX provides further extensibility by allowing you to write custom type converters. Any arbitrary type can be shaped to a JavaScript object through the use of a custom converter.

The ASP.NET AJAX Networking stack is central to leveraging asynchronous communications used to make more responsive and interactive web applications. ASP.NET AJAX leverages the XMLHttpRequest object, JSON object notation, and the object-oriented approach of the Microsoft AJAX Library to make AJAX development easier.


Chapter 7: ASP.NET AJAX Application Services

ASP.NET provides a rich set of application services that are of fundamental value when building web applications. Forms authentication makes it possible to easily authenticate users against virtually any credentials storage mechanism. There are built-in role management facilities for creating, deleting, and disabling user accounts. ASP.NET also includes the Profile feature for storing and retrieving personalization information about users. This can be used for both authenticated and anonymous users.

Before ASP.NET AJAX, these services were available only on the server. You could write code in your .NET Framework language of choice to authenticate users, manage roles, or work with the Profile personalization data. You could also make use of a set of controls for presenting the user with an interface for logging in, displaying their status, creating new users, or retrieving forgotten passwords. This could even be done without writing any code by using the default services provided by ASP.NET.

ASP.NET AJAX builds on the application services to expand the way they can be leveraged from JavaScript code running in the browser. Script classes provided as part of the Microsoft AJAX Library make it easy to authenticate users and store and retrieve Profile information. In this chap-ter, you will see how to configure the ASP.NET Application Services for use and examine the new JavaScript classes you use to access them from the client.

Authenticating Users

ASP.NET supports several schemes for authenticating users. It works with the IIS web server to coordinate the types of authentication used. In the default scheme, the authentication mode is set to Windows, which means that a WindowsPrincipal object is assigned to every request and used to check against the file and operating system restrictions in effect for that user. The WindowsPrincipal object is a type in the .NET Framework that represents a machine or domain authenticated user. Basic information identifying the user’s name and access rights is available from the WindowsPrincipal object.

<configuration>
    <system.web>
        <authentication mode="Windows" />
  </system.web>
</configuration>

The WindowsPrincipal assigned to the request can either be a low-privileged account created for ASP.NET or the identity of the logged-in user accessing the site. IIS can be configured to require the user be authenticated, and it supports impersonation so that the user’s identity is then passed on to ASP.NET.

The authentication mode can also be set to None to skip the attachment of the WindowsPrincipal object to the request. This does not mean that IIS authentication of the user is skipped but that ASP.NET is not requiring the authentication and won’t carry the delegated credentials. In version 2.0 of ASP.NET, the authentication schemes work together to provide rich infrastructure that carries across the static and dynamic content. ASP.NET also supports using the Passport Service (also known as the Windows Live ID service) to make use of a centralized and shared login.

To authenticate users without requiring Passport accounts or Windows accounts, ASP.NET also has an authentication mode called Forms. This is the most popular authentication type for Internet-facing sites, as opposed to Intranet-facing sites that commonly use Windows authentication with Active Directory user accounts. As Internet users cannot be expected to have Windows domain user accounts, it becomes necessary to prompt users for their credentials and then validate them against a database or some other mechanism. Forms authentication gets its name from the fact that the user typically provides his or her username and password in an HTML form submitted to the server where the authentication occurs. Forms authentication allows a site to have thousands of users without ever requiring them to have Windows accounts within the host domain. There is a tradeoff, though: You can’t use Windows access control lists to restrict file and resource access, so you must be careful about what resources you access when processing a web request on behalf of a user.

Configure Forms Authentication

First, Forms authentication must be enabled for the application. Otherwise, the content is not restricted and the request is not checked for the Forms authentication ticket issued to sessions when the user authenticates. To enable Forms authentication, set the mode attribute of the authentication element to Forms. This isn’t the only default value that needs to be changed. ASP.NET also allows anonymous users to access the site, so in order to force users to authenticate, you must also add authorization information to the web.config file to disallow anonymous users. The web.config file shown in the following code denies an anonymous user access while allowing all authenticated users access. This is done using special wildcard characters. The anonymous user is represented by the question mark, and all authenticated users are associated with the asterisk. Be careful not to confuse these symbols! The deny element excludes anonymous users indicated with the question mark, blocking all those that have not been through authentication. And the asterisk specified in the allow element indicates that all users who have been authenticated should be allowed. It’s a good practice to place the deny elements before the allow elements, since ASP.NET will stop examining these clauses when it finds a match on any one of them. You can also specify roles in the authorization elements to further control who is allowed where in a site. The roles are then checked against the ASP.NET Roles application service as shown in the follow-ing from a web.config file:

<?xml version="1.0"?>
<configuration>
  <appSettings/>
  <connectionStrings/>
  <system.web>
    <authentication mode="Forms">
      <forms cookieless="UseCookies" />
    </authentication>
    <authorization>
      <deny users="?"/>
      <allow users="*"/>
    </authorization>
  </system.web>
</configuration>

With this configuration, ASP.NET will now look at every request to the application, and if they have not authenticated, they will be redirected to the loginURL specified in the forms element of configuration. In this example, no loginURL is given, so the default login.aspx page will be used. Figure 7-1 shows the redirect that happens as a result of an anonymous user requesting a page called SecuredPage.aspx from the application.

Image from book
Figure 7-1

Notice that the error indicates that the requested URL was login.aspx. The URL in the address bar of the browser shows that the querystring contains a ReturnURL key-value pair that shows the original path requested. If the site had a page named login.aspx, the unauthenticated user would have been redirected there. Once the user provides credentials and is validated, ASP.NET will return him to the page they were originally targeting.

Custom Authentication

When authenticating users, you can leverage the built-in membership provider. Listing 7-1 is an excerpt from a custom membership provider (WroxMembershipProvider.cs). It shows that you can override the abstract MembershipProvider class to provide a custom implementation. This allows you to decide what action must be taken to validate a user.

Listing 7-1
Image from book

namespace Wrox.Samples {
    using System;
    using System.Web;
    using System.Configuration.Provider;
    using System.Web.Security;
    using System.Collections;
    using System.Collections.Specialized;

    public class WroxMembershipProvider : MembershipProvider {

        public override void Initialize(string name, NameValueCollection config) {
            base.Initialize(name, config);
        }

        public override bool ValidateUser(string username, string password) {
            return ((username == "WroxUser") && (password == "passw0rd"));

        }

        public override bool UnlockUser(string userName) {
            throw new System.NotImplementedException();
        }
    }
}
Image from book

The code in Listing 7-1 does not override all of the members of the abstract members as you would need to in providing a custom membership provider. In reality, you would want to use some better security practices than hardcoding usernames and passwords, as we did in the ValidateUser method earlier. It’s a best practice to apply a one-way hash to the user’s password and store that in the database instead. You can’t decrypt the password and send it to the user, but you minimize risk of someone discovering the password, since you’re not going to store it directly. To use the custom provider, you add the provider and also specify that you want your provider to be used by default in the web.config file.

<membership defaultProvider="SampleProvider">
    <providers>
      <add name="SampleProvider" type="Wrox.Samples.WroxMembershipProvider"/>
    </providers>
</membership>

Authenticating in JavaScript

Before using the Microsoft AJAX Library to authenticate users from JavaScript code running in the browser, you have to enable it. By default, the JavaScript services are not enabled. These services are off by default to keep all unneeded services disabled, which is a best practice from a security standpoint. You have to opt in to use these services.

<system.web.extensions>
    <scripting>
    <webServices>
      <authenticationService enabled="true" requireSSL="false"/>
    </webServices>
    </scripting>
</system.web.extensions>

In addition to enabling the Authentication service, you can specify whether or not to allow requests to be made without SSL encryption. Similarly, other ASP.NET services are not enabled for script access by default. Once enabled, the Sys.Services.AuthenticationService namespace will be available for use in the browser by all ASP.NET AJAX pages that use a ScriptManager component. JavaScript code can then call back to the ASP.NET authentication service without requiring a full postback or requiring a user to be redirected to a separate login page.

Listing 7-2 (from Membership.aspx) is a page with JavaScript code that uses the Authentication service to perform an out-of-band call to the login method to check the user’s credentials using the custom membership provider. The pageLoad function wires up an event handler for the Login button. The han-dler code then retrieves the username and password from textboxes on the page. It also declares variables for optional parameters. The isPersistent parameter is used to declare whether or not the authentication ticket issued should be persisted by the browser after it closes for use by a later session. If not set to true, the ticket is not kept beyond the current browser session. When the browser closes, the ticket is deleted.

Listing 7-2
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Membership Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:scriptmanager runat="server" id="scriptmanager" />
    Username <input type="text" id="username" /><br />
    Password <input type="password" id="password" /><br />
    <input type="button" value="Login" id="login" /><br />

    </div>
    </form>
</body>
<script type="text/javascript">
function pageLoad() {
    $addHandler($get('login'),
}

function loginHandler() {
    var username = $get('username')
    var password = $get('password')
    var isPersistent = false;
    var customInfo = null;
    var redirectUrl = null;
    Sys.Services.AuthenticationService.login(
       username,
        password,
        isPersistent,
        customInfo,
        redirectUrl,
        loginCompleted,
        loginFailed);
}

function loginCompleted(result, context, methodName) {
    if(result) {
        alert("Successfuly logged in.");
    }
    else {
        alert("Failed to login.");
    }
}

function loginFailed(error, context, methodName) {
    alert(error.get_message());
}

</script>
</html>
Image from book

The customInfo and redirectUrl are also optional. Whatever is passed in the customInfo argument will be relayed to the callback functions after the authentication is complete. The redirectUrl, when provided, indicates the page that the user should be sent to when the authentication check is complete. The other two arguments to the login method are the completed and failed callback functions. You pass the name of functions that should be called when the login call completes or fails. It’s important to note that the completed callback is called if the login completes, even if the result is a failed login attempt. The failed callback is called only if the login fails, however.

As with other ASP.Net services, you can define default handlers instead of specifying them with each call. Once you have created JavaScript functions you want to set as the defaults, you can make them the default for the page. The default loginCompletedCallback, logoutCompletedCallback, and failedCallback can all be set in the JavaScript pageLoad function of your page:

var service = Sys.Services.AuthenticationService;
service.set_defaultLoginCompletedCallback( loginComplete );
service.set_defaultLogoutCompletedCallback( logoutComplete );
service.set_defaultFailedCallback( authFailed );

You would still need to set the defaults on each page where the authentication service was going to be used, but you could standardize on a set of names as shown here and have those functions defined in a script included in every page.

Accessing User Status

Of course, performing a validation check to log in a user is only part of what is needed in a Membership service. You also need to be able to check the current status of a user. If the user is not logged in, you need to present the option to log in, and you should not try to get any other data that would apply only to an authenticated user. The get_isLoggedIn function calls the authentication service to get the user’s current status. In Listing 7-3 (Status.aspx) the user’s current status is shown as a result of clicking the button on the page.

Listing 7-3
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Membership Status Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:scriptmanager runat="server" id="scriptmanager" />
    <input type="button" value="Show Status" id="status" /><br />
    </div>
    </form>
</body>
<script type="text/javascript">
function pageLoad() {
    $addHandler($get('status'),
}

function statusHandler() {
    var result = Sys.Services.AuthenticationService.get_isLoggedIn();
    if(result === true) {
        alert('You are logged in.');
    }
    else {
        alert('You are NOT logged in.');
    }
}

</script>
</html>
Image from book

Logging Out

When a user chooses to log out, you may want to redirect her away from the page she is currently viewing. The logout function provides a redirectUrl parameter for this purpose. If set, the browser will be navigated to the redirectUrl when the logout call completes. Be aware that a page unload handler that might be used by the JavaScript code for a particular page that could interfere with this logout navigation. The unload code would need to be written knowing that logout could be called, and it must allow the redirect to happen. This unload issue is especially important if you have other developers working on the same application. Listing 7-4 (Logout.aspx) adds a button to the Status.aspx page for logging out.

Listing 7-4
Image from book
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Logout Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:scriptmanager runat="server" id="scriptmanager" />
    <input type="button" value="Show Status" id="status" /><br />
    <input type="button" value="Logout" id="logout" /><br />
    </div>
    </form>
</body>
<script type="text/javascript">
function pageLoad() {
    $addHandler($get('status'), 'click ', statusHandler);
    $addHandler($get('logout'), 'click ', logoutHandler);
}

function statusHandler() {
    var result = Sys.Services.AuthenticationService.get_isLoggedIn();
    if(result === true) {
        alert('You are logged in.');
    }
    else {
        alert('You are NOT logged in.');
    }
}

function logoutHandler() {
    var redirectUrl = 'Logout.aspx';
    var userContext = null;
    Sys.Services.AuthenticationService.logout(redirectUrl, logoutCompletedCallback,
  logoutFailedCallback, userContext);
}
function logoutCompletedCallback(result, context, methodName) {
    alert('Logged out.')
}
function logoutFailedCallback(error, context, methodName) {
    alert(error.get_message());
}

</script>
</html>
Image from book

The completion callbacks all have the same set of parameters. The first parameter is the result. This is the return value of the service method being called. If the method returns a Boolean, the result will be that Boolean it returned. If it returns void, as the Logout function does, the result argument will be null.

Now you can avoid sending users through a series of redirects to log in and log out. The ASP.NET AJAX Extensions make it easy to authenticate users, check their status, and log them out from any page!


User Profile Data

ASP.NET has a feature that allows you to easily track various kinds of data for each user. This is basically a persistent version of the ASP.NET session state storage mechanism. It is often used to remember per-user defaults between sessions, such as the particular skin a user likes, or even default values for forms. This can be enabled to store data only for authenticated users or to support anonymous users as well. You define the name and types of the data that you want to store in Profile data using the web.config file for the application. You can then set and retrieve the specific data values from your server-side code. Although the Profile feature is similar to the Session object, it goes beyond it in several key ways. By default, the data is stored in the built-in database available to ASP.NET applications, so it is automatically persisted between user sessions. Session data is not - it is stored in memory by default, but even if you choose to utilize SQL Server to hold session data, it will always be lost at the end of the user’s current session. Also, Profile data is strongly typed, making it easier to code against and avoiding some common developer errors. Session data is only treated as the basic object type and must be converted by you to the original type, leading to possible runtime exceptions if the wrong type is used by mistake. ASP.NET provides the aspnet_regsql.exe tool that can be used to configure a backend SQL Server with the tables necessary to store the profile information. The Profile information can also be made available in a server farm deployment scenario, since the database is always used as the persistence medium.

Defining Profile Data

The first step in using the Profile feature is to establish the names and types of the data that you want to track. The following section from a web.config file declares five properties to be tracked as Profile data for each user. Note that in addition to the name and type, you can control whether or not the data is tracked for anonymous users. This is useful when a subset of users may authenticate in parts of the application and you want to store information for only that group, so you may not want to hold data for anonymous users. Conversely, maybe you have some personalization features that you specifically want to open up to anonymous users. Therefore, if the same anonymous user visits your site again, he will see that you were able to remember some facts about him, even though he didn’t create a user account. Also, notice that you can define what type of serialization to utilize for storing the data as is done with the Artists.

<profile enabled="true" defaultProvider="SampleProvider">
  <providers>
       <add name="SampleProvider" type="Wrox.Samples.WroxProfileProvider" />
     </providers>
     <properties>
       <add name="Genre" type="System.String" allowAnonymous="true" />
       <add name="Artists" type="System.Collections.ArrayList"
allowAnonymous="true" serializeAs="Binary" />
       <add name="LastAlbumPurchased" type="Album" allowAnonymous="true"/>

       <group name="FavoriteAlbum">
         <add name="Artist" type="System.String" allowAnonymous="true" />
         <add name="Title" type="System.String" allowAnonymous="true"/>
       </group>
     </properties>
   </profile>

At the top level, you can have either items or groups. Groups are just a way to organize related items. The Title and Artist are in the FavoriteAlbum group. By grouping items together, you get nesting semantics, which can be especially helpful if there are a lot of Profile properties because Intellisense can help you find the item you’re looking for. If you don’t use groups and the list of Profile items is long, you may have to scroll through a lot of items to find what you want. Figure 7-2 shows that the Profile items become available in the code editor and Intellisense helps you select the desired items and groups.

Image from book
Figure 7-2

The type specified for the LastAlbumPurchased is just listed as Album, which does not exist in the .NET Framework. It is defined in a class located in the App_Code directory of the application. It is a simple class that tracks the artist, title, and year of release, as seen in Listing 7-5 (Album.cs). Because the default serialization will work fine for this class and its basic types, nothing special is required to configure or use it.

Listing 7-5
Image from book

using System;

public class Album
{
    private String _artist;
    private String _title;
    private int _releaseYear;

  public Album() {
  }

    public String Artist {
        get {
            return _artist;
        }
        set {
            _artist = value;
        }
    }

    public String Title {
        get {
            return _title;
        }
        set {
            _title = value;
        }
    }

    public int ReleaseYear {
        get {
            return _releaseYear;
        }
        set {
            _releaseYear = value;
        }
    }
}
Image from book

Accessing Profile Properties

Once the properties are defined, you can access them directly from the Profile intrinsic on any page. The Profile intrinsic is the object available in any ASP.NET page for retrieving and setting the properties for the current user that have been defined as part of the profile in the configuration. Another nice aspect of the Profile feature, as compared to using session state storage, is the strongly typed nature of the data.

In this example, an integer and string are stored in Session state. Because Session state treats everything as an object, they have to be explicitly cast to the desired type when they are read.

Session["count"] = 96;
Session["where"] = "anywhere";
count = (int)Session["count"];
where = (string)Session["where"];

If you were to declare the count and where variables are as Profile properties, the code becomes much easier to read and write.

Session["count"] = 96;
Session["where"] = "anywhere";
count = (int)Session["count"];
where = (string)Session["where"];

Listing 7-6 (Profile.aspx) is a sample .aspx page showing Profile storage in action. It links to other pages that show the current values of the Profile properties by loading them from the Profile storage and displaying them in the browser. There is also a page that makes updates to the Profile.

Listing 7-6
Image from book

<%@ Page Language="C#"%>
<script language="C#" runat="server">
protected override void OnLoad(EventArgs e) {
    Profile.FavoriteAlbum.Artist = "Phish";
    Profile.FavoriteAlbum.Title = "Rift";
    Profile.Genre = "Other";
    Profile.Artists.Clear();
    Profile.Artists.Add("Phish");
    Profile.Artists.Add("The Chemical Brothers");
    Profile.Artists.Add("Bryan Ferry");
    Profile.LastAlbumPurchased.Artist = "The Chemical Brothers";
    Profile.LastAlbumPurchased.Title = "Push The Button";
    Profile.LastAlbumPurchased.ReleaseYear = 2006;
}
</script>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:HyperLink runat="server" NavigateUrl="~/ShowProfile.aspx">
Show Profile</asp:HyperLink>
    <asp:HyperLink runat="server" NavigateUrl="~/PreLoadedProfileItems.aspx">
PreLoaded Profile Items</asp:HyperLink><br />
    <asp:HyperLink runat="server" NavigateUrl="~/LoadProfileItems.aspx">
Load Profile Items</asp:HyperLink><br />
    <asp:HyperLink runat="server" NavigateUrl="~/SaveProfileItems.aspx">
Save Profile Items</asp:HyperLink><br />
    </div>
    </form>
</body>
</html>
Image from book

When this page is requested, the Profile is populated with some music information, which is just hard-coded in the page’s Page_Load event handler. Clicking the Show Profile link takes you to a page (ShowProfile.aspx) that retrieves these same property values from the Profile and renders them to the browser. The result is shown in Figure 7-3.

Image from book
Figure 7-3

Accessing Profile Data from the Browser

So far, the examples have shown how to set up and use the Profile feature on the server. These are all tasks that can be done with ASP.NET 2.0. With ASP.NET AJAX, however, you can also access the Profile information directly from JavaScript code running in the browser! The Profile properties and groups have already been defined in the application’s configuration file. But this alone does not make the Profile data available remotely. You also have to explicitly opt in to the ability to use the Profile feature from the browser. This is done in the system.web.extensions section of the web.config file. In addition to setting enabled to true for the profileService element, you need to elaborate which properties should be made available. The read and write access permissions are controlled independently so that you can further control how the Profile data is used. If you know that you won’t need to modify Profile values on the client, you should omit the writeAccessProperties.

<system.web.extensions>
   <scripting>
     <webServices>
     <profileService enabled="true"
         readAccessProperties="Genre, Artists, FavoriteAlbum.Artist,
FavoriteAlbum.Title, LastAlbumPurchased"
         writeAccessProperties="Genre, Artists, FavoriteAlbum.Artist,
FavoriteAlbum.Title, LastAlbumPurchased" />
     </webServices>
   </scripting>
 </system.web.extensions>

ASP.NET AJAX adds a new handler for access to the Membership and Profile services. For IIS versions earlier than 7, or IIS7 running in the Classic mode, this setting is in the httpHandlers configuration section under system.web.

      <system.web>
       <httpHandlers>
       <remove verb="*" path="*.asmx"/>
       <add verb="*" path="*_AppService.axd" validate="false"
 type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions,
 Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
       </httpHandlers>
       </system.web>

And for the IIS7 Integrated mode, the setting is in the system.webserver section in the handlers element.

    <system.webserver>
    <handlers>
      <remove name="WebServiceHandlerFactory-ISAPI-2.0"/>
      <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd"
preCondition="integratedMode"
           type="System.Web.Script.Services.ScriptHandlerFactory,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
    </handlers>
    </system.webserver>

The configuration is now in place. You have defined a set of Profile properties, enabled the service for remote access, and declared the properties to be both readable and writeable. Now you need a page that accesses the Profile store from JavaScript.

Preload Profile Properties

The set of data being stored in the Profile can be quite extensive. It would be wasted overhead to send all of the Profile information down to the browser in the original page rendering if there’s only a small chance that you’ll need it all. The default is not to preload any Profile properties, but this isn’t ideal for the pages where you know you will need at least some Profile data. The ScriptManager’s ProfileService element has a LoadProperties attribute that allows you to specify what properties should be made available initially and sent down with the first page rendering.

In this example, the Genre and Artists would be available in the initial page rendering. Client script could make use of them without making any calls to the server to get more data. However, access to any other Profile data will still require going back to the server.

<asp:ScriptManager runat="server" ID="scriptManager">
    <ProfileService LoadProperties="Genre, Artists" />
</asp:ScriptManager>

The preloaded properties can be accessed directly from the properties object of the ProfileService object, which is part of the Microsoft AJAX Library.

var artists = Sys.Services.ProfileService.properties.Artists;

Thankfully, there is no requirement to provide callback functions or to pause and wait for an asynchronous call to the server to return the data for the preloaded properties. Listing 7-7 (PreLoadedProfileItems.aspx) is a page that adds a button and binds a click handler to it. The han-dler then shows the Genre and Artists Profile properties declared for preloading.

Listing 7-7
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>PreLoaded Profile Items</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager">
        <ProfileService LoadProperties="Genre, Artists" />
    </asp:ScriptManager>
    <div>
    <input type="button" id="showProfile" value="Show Profile" />
    </div>
    </form>
</body>
<script type="text/javascript">
function pageLoad() {
    $addHandler($get('showProfile'),
}

function showProfile() {
    var genre = "Favorite Genre : " + Sys.Services.ProfileService.properties.Genre;
    alert(genre);
    var artists = Sys.Services.ProfileService.properties.Artists;
    var message = "Favorita Artists:\r\n";
    for(var index = 0; index < artists.length; index++) {
        message += artists[index] + "\r\n";
    }
    alert(message);
}
</script>
</html>
Image from book

Figure 7-4 shows the Artists displayed by the browser. The ArrayList on the server is translated into a JavaScript object, where the individual elements of the ArrayList are accessed through the object’s indexer.

Image from book
Figure 7-4

Load Profile Properties

All of the Profile properties you might want to access while the page is running do not have to be preloaded. As long as they are declared in the web.config file as accessible remotely, Profile properties can be loaded on demand at any time. However, when making a call to retrieve those properties that were not preloaded, you need to provide a callback function, since there’s no way to predict how long that data-fetching operation will take. You can also make a call back to the server to refetch preloaded values, in case their value may have been changed since the page was loaded.

There are four arguments to the load method of the ProfileService. The first argument is an array of the names of the properties to be retrieved. The second is the callback function for completing the call. The third is the error callback. The last argument is for any user context data that should be passed to the completion events.

Sys.Services.ProfileService.load(
    ["FavoriteAlbum.Artist", "FavoriteAlbum.Title"],       loadCompletedCallback,
      failedCallback,
      null );

It is not necessary to pass anything for the userContext parameter unless you want to tell your callback something extra about this particular call. In this example, I include the fourth parameter with a null that is actually redundant. JavaScript implies a null value for any parameter that you don’t explicitly provide. I like to be explicit about it, as doing so helps me make fewer mistakes by thinking through what is needed and what is not. It’s also easier to come back to this code and add some context data in place of the null because I can see where that value should go.

The arguments to your completion callback function are the result of the call to the server method, the userContext that was passed to the load function, and the name of the method that was called. In this case, the result is the count of properties that were loaded.

function loadCompletedCallback(result, context, methodName) {
    alert("Retrieved " + result + " profile properties.");
}

The AJAX error object passed to the failed callback provides access to the type of exception that occurred and its error message. You can also get the stack trace in effect at the time the exception occurred.

function failedCallback(error, context, methodName) {
    var message = error.get_exceptionType() + "\r\n" +
        error.get_message() + "\r\n" +
        error.get_stackTrace();
    alert(message);
}
Image from book
Figure 7-5

The page in Listing 7-8 (LoadProfileItems.aspx) has two buttons: one for loading Profile data and the other for displaying it. It binds the event handlers to the button click events in the pageLoad method. It also adds an error handler to display more detailed information if something goes wrong. Displaying a stack trace may be helpful during development, but when the application is deployed, the end user won’t be able to do anything useful with the information, so more robust and user-friendly error recovery should be added before final production deployment.

Listing 7-8
Image from book
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Load Profile Items</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager">
        <ProfileService/>
    </asp:ScriptManager>
    <div>
    <input type="button" id="loadFavorite" value="Load Favorite" /><br />
    <input type="button" id="showFavorite" value="Show Favorite" />
    </div>
    </form>
</body>
<script type="text/javascript">
function pageLoad() {
    $addHandler($get('[showFavorite'), 'click ', showFavorite);
    $addHandler($get('loadFavorite'), 'click ', loadFavorite);

}
function showFavorite() {
    alert("Favorite Album\r\n" +
        Sys.Services.ProfileService.properties.FavoriteAlbum.Title +
        " by " +
        Sys.Services.ProfileService.properties.FavoriteAlbum.Artist);
}

function loadFavorite() {
    Sys.Services.ProfileService.load(["FavoriteAlbum.Artist",
"FavoriteAlbum.Title"], loadCompletedCallback, failedCallback, null);
}

function loadCompletedCallback(result, context, methodName) {
    alert("Retrieved " + result + " profile properties.");
}

function failedCallback(error, context, methodName) {
    var message = error.get_exceptionType() + "\r\n" +
        error.get_message() + "\r\n" +
        error.get_stackTrace();
    alert(message);
}
</script>
</html>
Image from book

Saving Profile Data

When Profile data is updated in the browser, it needs to be saved back to the server to be persisted for later use. The signature of the save function is the same as that of the load function. The first argument now indicates what set of Profile properties should be saved. If you don’t include a set of properties, all of the properties on the client will be sent. This can be overkill if only a few are actually in need of being updated.

function saveFavorite() {
    var profile = Sys.Services.ProfileService;
    profile.properties.Artists[0] =
    profile.save(["Artists"], saveCompletedCallback, failedCallback, null);
}

When working with custom types, like the Album example, there are a few additional requirements to be aware of. The Profile Service counts on the custom type having a parameterless constructor and property setters for each custom type that it creates. This is a standard requirement in order to deserialize data. The Profile Service doesn’t make other assumptions about how to deserialize data beyond requiring a list of name-value pairs for creating the type. To create an Album object to be saved in the Profile, the Artist, Title, and ReleaseYear are specified along with the values. The syntax can seem a little odd at first, but the simplicity avoids the need for any custom serialization.

function addLastPurchased() {
    var profile = Sys.Services.ProfileService;
    profile.properties.LastAlbumPurchased =
       { Artist: "Irina", Title: "ala riko kaavaa", ReleaseYear: "2001" };
    profile.save(
        ["LastAlbumPurchased"],
        saveCompletedCallback,
        failedCallback,
        null );
}

When updating the properties of a custom type that was already loaded, the name-value pair syntax is not necessary. The properties of custom objects are accessed in the same way as common .NET Framework types. You can access the property directly on the Profile property.

var lastArtistPurchased =
Sys.Services.ProfileService.properties.LastAlbumPurchased.Artist;

Or you can access the Profile’s version of the custom type and pass the name of the property as an indexer argument. This would be most useful if the name of the property were being extracted from a JavaScript variable.

var albumTitle =
Sys.Services.ProfileService.properties.LastAlbumPurchased["Title"];

The key thing to remember when working with custom types that are part of the Profile is that in the JavaScript representation, no custom serialization or advanced type conversion occurs. Instead, the properties are reflected as simple types when sent down to the browser. And when they are sent to the server, they are treated as name-value pairs, where the name indicates the property accessor to invoke, and the value is the argument to pass to the property setter. Listing 7-9 (SaveProfileItems.aspx) is a page that displays Profile information, allows the user to modify the first element of the Artists array, and updates the last album purchased in a button’s click event handler.

Listing 7-9
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Save Profile Items</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager">
        <ProfileService LoadProperties="Artists, LastAlbumPurchased" />
    </asp:ScriptManager>
    <div><b>Favorite Artist : </b><input type="text" id="favoriteArtist" /><br />
    <input type="button" id="saveFavorite" value="Save" /><br /><br />
    <asp:Label runat="server" ID="artists" /><br /><br />
    <input type="button" id="addLast" value="Add Album"/>
    <asp:Label runat="server" ID="lastArtistPurchased" />
    </div>
    </form>
</body>
<script type="text/javascript">
var favoriteArtist = "";

function pageLoad() {
    $get('favoriteArtist').value =
Sys.Services.ProfileService.properties.Artists[0];
    $addHandler($get('saveFavorite'),
    $addHandler($get('addLast'),
    updateLabel();
}

function updateLabel() {
    var theArtists = Sys.Services.ProfileService.properties.Artists;
    var theLabel = "";
    for(var i = 0; i < theArtists.length; i++) {
        theLabel += theArtists[i] + "<br />";
    }
    $get('artists').innerHTML = theLabel;
    $get('lastArtistPurchased').innerHTML
Sys.Services.ProfileService.properties.LastAlbumPurchased["Artist"];
}

function saveFavorite() {
    var profile = Sys.Services.ProfileService;
    profile.properties.Artists[0] =
    profile.save(["Artists"], saveCompletedCallback, failedCallback, null);
}

function addLastPurchased() {
    var profile = Sys.Services.ProfileService;
    profile.properties.LastAlbumPurchased =
       { Artist: "Irina", Title: "ala riko kaavaa", ReleaseYear: "2001" };
    profile.save(
        ["LastAlbumPurchased"],
        saveCompletedCallback,
        failedCallback,
        null );
}

function saveCompletedCallback(result, context, methodName) {
    updateLabel();
    alert("Saved " + result + " profile properties.");
}

function failedCallback(error, context, methodName) {
    var message = error.get_exceptionType() + "\r\n" +
        error.get_message() + "\r\n" +
        error.get_stackTrace();
    alert(message);
}

</script>
</html>
Image from book

Profile data, whether they are simple types or complex custom types, can now be accessed easily from JavaScript code in the browser. You can retrieve and update data on the fly without a full postback of the page.

Image from book
Security Concerns about Client-side Profile Usage

You need to be aware that if you allow Profile data to be read or written from the client, a malevolent hacker might intercept the data flow in either direction. Conversely, if the Profile data is only accessed from the server, there would be no chance of interception. For this reason, you must be careful not to allow any sensitive data to be passed to or from the client in Profile storage items. Of course, writable properties are going to be your biggest concern, since readable values will likely be rendered as viewable HTML anyway. The best usage of Profile storage is only to hold user preferences, such as a favorite skin, color scheme, or default values for GUI controls, or nonsensitive user data that would not cause a problem if compromised. For example, the user’s favorite background color is fine, but his bank account number would probably not be a good candidate for Profile storage.

Image from book


Summary

ASP.NET AJAX brings two important application services to JavaScript code in the browser. It is no longer necessary to think of Forms authentication and Profile as server-only features. The application services simply leverage server resources for checking user credentials and for storing Profile data.

You can now more easily enhance applications to gather and store data about the user, even if he or she is not authenticated, by enabling Profile properties for anonymous use. And when the user returns to the site, the Profile information will still be available. In addition, when a user chooses to authenticate, he can do so directly from the browser without a series of redirects to a login page and back.

Enabling the application services for AJAX is an important step forward in being able to write richer browser-based applications that take full advantage of the server and to enhance the user’s experience. The application can seem more dynamic and responsive by reducing the need to do a full postback and refresh of entire pages.


Chapter 8: Rich AJAX Toolkit Controls

Overview

ASP.NET AJAX is the foundation on which to build richer web applications that leverage the browser more fully but it doesn’t have the rich UI elements that really blur the distinction between web and desktop applications. With the Microsoft AJAX Library, you can apply familiar object-ori-ented design concepts and use the scripts across a variety of modern browsers. The ASP.NET 2.0 AJAX Extensions 1.0 include several powerful ASP.NET controls that make it easy to add AJAX functionality to an existing application or build better user experiences into a new application, but the AJAX Toolkit was developed to provide some rich ASP.NET AJAX controls that you can use to make your web applications really come to life. The Toolkit makes it easy to push the user interface of an application beyond what users expect from a web application.

The Toolkit is a shared source project with code contributions from developers from Microsoft and elsewhere. Most developers who download the AJAX Extensions should also download the Toolkit for the additional set of controls it contains. Information is available at http://ajax.asp.net/ajaxtoolkit. The CodePlex project mentioned by this page allows you to download a compiled dll with the controls and extenders, or you can download the source code and project files and compile it yourself. But either way, make sure you add the dll to your ToolBox in Visual Studio, as described in the documentation.

The Toolkit contains some new controls that have AJAX functionality and a lot of control exten-ders. The control extenders attach to another control to enhance or “extend” the control’s functionality. When you install the Toolkit, it creates a sample web application that has some samples of using the controls. Because the controls cover such a wide variety of application-development areas, they are presented here in three categories: page layout controls, user interface effects, and pop-ups. Within each category, the controls are listed alphabetically; the control names are self-explanatory, so you’ll find it easy to locate the information you need when using this chapter for later reference.

Also, note that the Toolkit project is ongoing and will continue to evolve as developers contribute to it. This chapter is up to date as of the time of this writing, but I expect that more will be added to the Toolkit regularly.


Controlling Page Layout

Screen real estate is limited. Yes, screen resolution has improved over time, but with lots of information to present, and in the confines of the web browser, you want to have the best control possible over how the data is presented to the user.

The Accordion, AlwaysVisibleControl, CollapsiblePanel, ResizableControl, and Tab controls give you control over the page layout while adding interactivity for the user. Users can better control what they are viewing at any given time while still having easy access to other data on the page.

Accordion

The Accordion control is used to specify a set of panes, similar to the famous menu in Microsoft Outlook. Each pane is made up of a header template and a content template. The header templates of all panes are always visible, while only one content template is visible. The user selects which pane to view by clicking on the header. The content from the previously active pane is hidden from view, and the content of the newly selected pane is displayed instead.

The Accordion control can provide a fade transition when switching among active panes. You set the FadeTransitions property to true and can set the TransitionDuration and FramesPerSecond values. The default values are 250 milliseconds and 40 frames per second, respectively.

The SelectedIndex property lets you declaratively and programmatically control which pane to show. Other important properties are the AutoSize and Height properties. The AutoSize property is None by default, meaning that the size of the Accordion control changes based on the active pane. Other content on the screen may be shifted to accommodate the changing size. But when the AutoSize property is set to Limit, the size is restricted to the Height value. The active pane will display scroll bars if the content is larger than the space available. The other possible value is Fill, which will result in expanding a pane if the content is not large enough to satisfy the Height value provided. Listing 8-1 (Accordion.aspx) shows the Accordion control in action. The Accordion control is used with four panes. The header for each pane shows the title of an album and the content shows the track listings.

Listing 8-1
Image from book

<%@ Page Language="C#" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Accordion</title>
</head>
<body style="font:verdana">
    <form id="form1" runat="server">
    <asp:ScriptManager ID="scriptManager" runat="server" />
    <div>
    <ajaxToolkit:Accordion runat="server" id="theAccordion"
        SelectedIndex="0"
        AutoSize="Limit"
        FadeTransitions="true"
        TransitionDuration="333"
        FramesPerSecond="30"
        Height="460px" >
    <Panes>

    <ajaxToolkit:AccordionPane ID="AccordionPane3" runat="server">
    <Header><br /><div style="background:gray; font-size:larger; font
weight:bold">Phish: Lawn Boy</div></Header>
    <Content><div style="background:#336699">
    <ul>
    <li>The Squirming Coil</li>
    <li>Reba</li>
    <li>My Sweet One</li>
    <li>Split Open and Melt</li>
    <li>The Oh Kee Pa Ceremony</li>
    <li>Bathtub Gin</li>
    <li>Run Like an Antelope</li>
    <li>Lawn Boy</li>
    <li>Bouncing Around the Room</li>
    </ul></div>
    </Content>
    </ajaxToolkit:AccordionPane>

    <ajaxToolkit:AccordionPane ID="AccordionPane4" runat="server">
    <Header><br /><div style="background:gray; font-size:larger; font
weight:bold">Phish: Rift</div></Header>
    <Content><div style="background:#336699">
    <ul>
    <li>Rift</li>
    <li>Fast Enough for You</li>
    <li>Lengthwise</li>
    <li>Maze</li>
    <li>Sparkle</li>
    <li>Horn</li>
    <li>The Wedge</li>
    <li>My Friend, My Friend</li>
    <li>Weigh</li>
    <li>All Things Reconsidered</li>
    <li>Mound</li>
    <li>It's Ice</li>
    <li>Lengthwise</li>
    <li>The Horse</li>
    <li>Silent in the Morning</li>
    </ul></div>
    </Content>
    </ajaxToolkit:AccordionPane>

    <ajaxToolkit:AccordionPane ID="AccordionPane1" runat="server">
    <Header><br /><div style="background:gray; font-size:larger; font
weight:bold">Phish: Junta Disc 1</div></Header>
    <Content><div style="background:#336699">
    <ul>
    <li>Fee</li>
    <li>You Enjoy Myself</li>
    <li>Esther</li>
    <li>Golgi Apparatus</li>
    <li>Foam</li>
    <li>Dinner and a Movie</li>
    <li>The Divided Sky</li>
    <li>David Bowie</li>
    </ul></div>
    </Content>
    </ajaxToolkit:AccordionPane>

    <ajaxToolkit:AccordionPane ID="AccordionPane2" runat="server">
    <Header><br /><div style="background:gray; font-size:larger; font
weight:bold">Phish: Juna Disc 2</div></Header>
    <Content><div style="background:#336699">
    <ul>
    <li>Fluffhead</li>
    <li>Fluff's Travels</li>
    <li>Contact</li>
    <li>Union Federal [Live]</li>
    <li>Sanity [Live]</li>
    <li>Icculus [Live]</li>
    </ul></div><br />
    </Content>
    </ajaxToolkit:AccordionPane>

    </Panes>
    </ajaxToolkit:Accordion>
    </div>
</form>
</body>
</html>
Image from book

The output of running the page from Internet Explorer and Firefox is shown in Figure 8-1. In Internet Explorer on the left, I have selected the second album header, so the track listing of that album is shown. On the right in Firefox, I have selected the third album, and the Accordion control has automatically hidden the previously visible album listing and shows the one now selected instead.

Image from book
Figure 8-1

AlwaysVisibleControlExtender

When presenting information in the browser, you may want to keep a piece of information fixed in the user’s view. Screen space is a limited commodity, and there are cases where something should always be available without ever having to scroll. The AlwaysVisibleControlExtender lets you designate any ASP.NET control as having this distinction. You specify a position for the control using the AlwaysVisibleControlExtender, and while the user scrolls the page to view other information, the control you designate is always kept in view. It seems to move around as the user scrolls the screen or resizes the window, so that it stays in the same relative position in the viewable portion of the browser window.

The AlwaysVisibleControlExtender has only six properties. Listing 8-2 (AlwaysVisibleControl.aspx) shows setting all six to nondefault values. The TargetControlID is set to a label. Following the recommendation of the Toolkit developers, the label has been given a position using CSS to minimize flashing as it is repositioned during scrolling. The VerticalSide is set to Top (the options are Top, Middle, and Bottom). The VerticalOffset default is 0 but is set to 10px for some spacing. The HorizontalSide is set to Right (the options are Left, Right, and Center), and again the HorizontalOffset is adjusted up. Finally, the ScrollEffectDuration is set to one second. This controls the duration of the animation effect shown when the control is repositioned during scrolling.

Listing 8-2
Image from book

<%@ Page Language="C#" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>AlwaysVisibleControlExtender</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager" />
    <div>
    <asp:Label runat="server" ID="todaysAlbum" BorderStyle="solid"
style="horizontal-align:right; border-spacing:10px">
Today's Album:<br />Rift by
    <ajaxToolkit:AlwaysVisibleControlExtender
ID="AlwaysVisibleControlExtender1" runat="server"
        TargetControlID="todaysAlbum"
        VerticalSide="Top"
        VerticalOffset="10"
        HorizontalSide="Right"
        HorizontalOffset="10"
        ScrollEffectDuration="1.0" /><br />
    </div>
    <div style="width:400px; style="horizontal-align:left" >
    In the word of music today, some very interesting albums were released.
One by an up and coming blues singer,    another by a new to the scene jazz
pianist.
In the world of Rock and Roll, somebody smashed up a hotel room
    while somebody else's tour bus
On the Country scene, etc.<br /><br />

    In the word of music today, some very interesting albums were released.
One by an up and coming blues singer,another by a new to the scene jazz pianist.
In the world of Rock and Roll, somebody smashed up a hotel room
    while somebody else's tour bus
On the Country scene, etc.<br /><br />
    </div>
    </form>
</body>
</html>
Image from book

You can see the effect of the AlwaysVisibleControlExtender in Figure 8-2. Today’s Album selection is kept in view in the upper right, while the other text is allowed to scroll.

Image from book
Figure 8-2

CollapsiblePanelExtender

The CollapsiblePanelExtender is similar to the Accordion control but does not target multiple content areas. An ASP.NET panel control is shown or hidden from view based on the user’s interaction with a given control. This allows you to hide something the user doesn’t always need to see. The TargetControlID is shown when the ExpandControlID is clicked or hidden when the CollapseControlID is clicked. Alternatively, it can be shown or hidden based on a mouse hover if the AutoCollapse and AutoExpand properties are set to true.

Listing 8-3 (CollapsiblePanel.aspx) demonstrates the use of a CollapsiblePanelExtender to set the panel size to 0 when it is collapsed and to 300 pixels when it is expanded. Another panel is used as the selector for expanding and collapsing the panel. And a label is included that is designated as the TextLabelID. The value of the label is changed between the ExpandedText and CollapsedText values based on the current state.

Listing 8-3
Image from book
<%@ Page Language="C#" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>CollapsiblePanel</title>
</head>
<body style="background:gray">
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager1" />
    <div style="border-spacing:25px;background:white">
    <asp:Panel runat="server" ID="panel1">
    <div style="font-weight:bold">
    Clicking Here controls the CollapsiblePanel
<asp:Label runat="server" ID="textLabel" />
    </div>
    </asp:Panel>
    <asp:Panel runat="server" ID="panel2">
    <div>
    And this is the body of the collapsible panel
where information that is shown only when the panel is expanded.<br /><br />
    The user can click to expand a panel or you can configure the panel to expand
    automatically when the user hovers the mouse over the panel.
    </div>
    </asp:Panel>
    <ajaxToolkit:CollapsiblePanelExtender runat="server" id="cpe"
        TargetControlID="panel2"
        CollapseControlID="panel1"
        ExpandControlID="panel1"
        Collapsed="true"
        CollapsedSize="0"
        ExpandedSize="300"

        ExpandedText="(Collapse...)"
        CollapsedText="(Expand...)"
        TextLabelID="textLabel"

    >
    </ajaxToolkit:CollapsiblePanelExtender>
    </div>
    </form>
</body>
</html>
Image from book

ResizableControl

In many situations, you may want to limit the size of an element when it is initially displayed but allow the user to grow or shrink the element as they see fit. The ResizableControl makes this easy. You place the ResizableControl on the page and point it to an ASP.NET Panel control using the TargetControlID property.

You use the HandleCssClass property to specify the style information about the appearance of the han-dle the user selects to begin resizing the panel. The ResizableCssClass property refers to style information shown while the panel is being altered.

The control also exposes events that are raised that you can attach code to in order to react to the panel being resized: OnClientResizeBegin, OnClientResizing, and finally OnClientResize. These are very useful for actions such as altering text size or retrieving additional data if the panel is grown or hid-ing elements if the panel is shrunk. Listing 8-4 (ResizableControl.aspx) is an example of using the ResizableControl with the CSS information inline in the page.

Listing 8-4
Image from book
<%@ Page Language="C#" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Resizable Control</title>
    <style type="text/css">
    .handle {
        width:10px;
        height:10px;
        background-color:#aaccee;
    }
    .resizing {
     padding:0px;
     border-style:solid;
     border-width:3px;
     border-color:#aaccee;
    }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager" />
    <div>
    <asp:Panel ID="Panel1" runat="server" style="width:200px;height:100px;">
        <asp:Image runat="server" ImageUrl="~/images/ajax.gif" ID="image1"
style="width:100%;height:100%;" />
    </asp:Panel>
    <ajaxToolkit:ResizableControlExtender
ID="ResizableControlExtender1" runat="server"
        TargetControlID="Panel1"
        HandleCssClass="handle"
        ResizableCssClass="resizing"
        MaximumHeight="260"
        MinimumHeight="60"
        MaximumWidth="300"
        MinimumWidth="80"
        HandleOffsetX="4"
        HandleOffsetY="4"
    />
    </div>
    </form>
</body>
</html>
Image from book

Figure 8-3 shows the size taken from the image when the page is initially requested, followed by the change after it has been resized to the maximum size specified in the ResizableControl.

Image from book
Figure 8-3

Tabs

The TabContainer and TabPanel controls make it easy to present the familiar tabbed UI. The user is presented with a set of tabs across the top of a single pane of content displayed for the active tab. When the user selects a different tab, the content is changed.

The TabContainer allows you to attach a server event called the ActiveTabChanged event that is fired during a postback if the active tab has changed. You can also use the OnClientActiveTabChanged event to have your JavaScript event triggered in the browser when the user selects a different tab. The ScrollBars property lets you designate whether scrollbars should be Horizontal, Vertical, Both, None, or when set to Auto the control makes the determination.

The TabPanel control has a HeaderTemplate for the tab and a ContentTemplate for the body. You can forego using the HeaderTemplate and specify the HeaderText property instead. It also has an event that will be triggered when the tab is selected called OnClientClick. One particularly interesting feature of the Tabs feature is the ability to disable tabs programmatically in JavaScript in the browser by setting the Enabled property to false. Listing 8-5 (Tabs.aspx) adapts the example used in Listing 8-1 for the Accordion control for use with tabs.

Listing 8-5
Image from book

<%@ Page Language="C#" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Tabs</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager" />
    <div>
    <ajaxToolkit:TabContainer runat="server" ID="tabContainer" Height="420" >
    <ajaxToolkit:TabPanel runat="server" ID="panel1" HeaderText="Lawn Boy">
    <ContentTemplate>
    <ul>
    <li>The Squirming Coil</li>
    <li>Reba</li>
    <li>My Sweet One</li>
    <li>Split Open and Melt</li>
    <li>The Oh Kee Pa Ceremony</li>
    <li>Bathtub Gin</li>
    <li>Run Like an Antelope</li>
    <li>Lawn Boy</li>
<li>Bouncing Around the Room</li>
</ul>
</ContentTemplate>
</ajaxToolkit:TabPanel>

<ajaxToolkit:TabPanel runat="server" ID="panel2" HeaderText="Rift">
<ContentTemplate>
<ul>
<li>Rift</li>
<li>Fast Enough for You</li>
<li>Lengthwise</li>
<li>Maze</li>
<li>Sparkle</li>
<li>Horn</li>
<li>The Wedge</li>
<li>My Friend, My Friend</li>
<li>Weigh</li>
<li>All Things Reconsidered</li>
<li>Mound</li>
<li>It's Ice</li>
<li>Lengthwise</li>
<li>The Horse</li>
<li>Silent in the Morning</li>
</ul>
</ContentTemplate>
</ajaxToolkit:TabPanel>

<ajaxToolkit:TabPanel runat="server" ID="panel3" HeaderText="Junta Disc 1">
<ContentTemplate>
<ul>
<li>Fee</li>
<li>You Enjoy Myself</li>
<li>Esther</li>
<li>Golgi Apparatus</li>
<li>Foam</li>
<li>Dinner and a Movie</li>
<li>The Divided Sky</li>
<li>David Bowie</li>
</ul>
</ContentTemplate>
</ajaxToolkit:TabPanel>

<ajaxToolkit:TabPanel runat="server" ID="panel4" HeaderText="Junta Disc 2">
<ContentTemplate>
<ul>
<li>Fluffhead</li>
<li>Fluff's Travels</li>
<li>Contact</li>
<li>Union Federal [Live]</li>
<li>Sanity [Live]</li>
<li>Icculus [Live]</li>
</ul>
    </ContentTemplate>
    </ajaxToolkit:TabPanel>

    </ajaxToolkit:TabContainer>

    </div>
    </form>
</body>
</html>
Image from book


UI Effects

Dynamic HTML is the notion of scripting DOM elements in the browser to do more interactive things. It is at the heart of AJAX. You dynamically update the page based on user actions. This goes beyond getting refreshed data from the server asynchronously. You can create rich animations and transitions from old to new data. The ASP.NET AJAX Toolkit makes it easy to declaratively compose a rich set of anima-tions into your application and to make the user interface better in ways typically associated with desktop applications. The following sections offer some examples of how you can push your application further through the use of the UI effects available in the Toolkit.

Animations

The ASP.NET AJAX Toolkit offers a broad set of animations that you can use programmatically and declaratively. You can attach the AnimationExtender directly to the control that will be affected, or you can animate a different control based on user input.

The standard set of animation events are OnLoad, OnClick, OnMouseOver, OnMouseOut, OnHoverOver, and OnHoverOut. The OnLoad animation occurs when the page is loaded. And, as you would expect, the OnClick animation happens when the user clicks the target control. The difference between the Mouse and Hover events is that the start of the OnHoverOver animation terminates the OnHoverOut animation and vice versa.

You should experiment with the ways that you can combine various animations to create the effect you want. There are animations for sizing, hiding, interpolating between values, moving, styling, fading, and more. In Listing 8-6 (Animation.aspx), I have some text in a panel that fades out when the user clicks it. It fades back in when the mouse is moved off the element, and it pulses briefly when the mouse hovers over it. As you can see, all of this is done with just a few lines of declarative code in the page. I didn’t have to write any JavaScript to control the DOM or modify the styles at all.

Listing 8-6
Image from book

<%@ Page Language="C#" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Animation</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager" />
    <div>
    <asp:Panel runat="server" ID="thePanel" style="backgroundColor: #ffffff;
width:150px; border: solid 1px; padding: 15px; cursor: pointer">
    <h3>This is the animation target.</h3>
    </asp:Panel>

    <ajaxToolkit:AnimationExtender id="animationExtender"
TargetControlID="thePanel" runat="server"  >
    <Animations>
        <OnClick>
            <Sequence>
                <FadeOut Duration="1.0" Fps="20" />
            </Sequence>
        </OnClick>

        <OnHoverOut>
            <Parallel>
                <FadeIn Duration="1.0" Fps="20" />
            </Parallel>
        </OnHoverOut>

        <OnHoverOver>
            <Sequence>
                <Pulse Duration="0.2" Fps="20" Iterations="5"
MinimumOpacity="0.2" MaximumOpacity="1.0" />
            </Sequence>
        </OnHoverOver>

    </Animations>
    </ajaxToolkit:AnimationExtender>
    </div>
    </form>
</body>
</html>
Image from book

The animations for an event can be defined to run all at once using the <parallel> element or to run one after another using the <sequence> element.

UpdatePanelAnimation

Animating an UpdatePanel as its content is being refreshed is a common scenario. The UpdatePanelAnimationExtender allows you to use the broad set of animations available in the Toolkit and will automatically coordinate playing them when the specified UpdatePanel is being updated or when the update has completed.

Listing 8-7 (UpdatePanelAnimation.aspx) is an example of using the UpdatePanelAnimationExtender. The TargetControlID is set to the UpdatePanel and the FadeIn and FadeOut animations attached to the <div> that contains the UpdatePanel.

Listing 8-7
Image from book
<%@ Page Language="C#" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    protected override void OnLoad(EventArgs e) {
        base.OnLoad(e);
        theLabel.Text = DateTime.Now.ToString();
        System.Threading.Thread.Sleep(2000);
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>UpdatePanelAnimation</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager" />
    <div style="background-color:#aaccee; padding-left:10px; padding:10px;
width:200px" id="UpdatePanelContainer" >
    <asp:UpdatePanel runat="server" ID="UpdatePanel1" >
    <ContentTemplate>
    <asp:label runat="server" id="theLabel" /><br />
    <input type="submit" value="Go" />
    </ContentTemplate>
    </asp:UpdatePanel>
    <ajaxToolkit:UpdatePanelAnimationExtender ID="UpdatePanelAnimation1"
runat="server" TargetControlID="UpdatePanel1" >
    <Animations>
        <OnUpdating>
            <Sequence duration=".25" Fps="30">
               <FadeOut
                AnimationTarget="UpdatePanelContainer"
                minimumOpacity="0.15" />
            </Sequence>
        </OnUpdating>

        <OnUpdated>
            <Sequence>
               <FadeIn
                AnimationTarget="UpdatePanelContainer"
                minimumOpacity="0.15" />
            </Sequence>
        </OnUpdated>
    </Animations>
    </ajaxToolkit:UpdatePanelAnimationExtender>

    </div>
    </form>
</body>
</html>
Image from book

When the UpdatePanel begins updating, the div is faded. When the call to the server is complete, the animation to fade the div back into view is run.

DropShadow

The DropShadowExtender allows you to add a DropShadow effect to an ASP.NET Panel on the page. You set the TargetControlID and can then control the Width, Opacity, and whether or not the corners should be Rounded. If the panel can be moved or resized, you can also set the TrackPosition property to true to indicate that JavaScript should run to track the panel and update the DropShadow as needed. This code adds a DropShadow to the image used in Listing 8-4 for the ResizableControl.

<ajaxToolkit:DropShadowExtender ID="DropShadowExtender1" runat="server"
    TargetControlID="Panel1"
    Opacity="0.6"
    Rounded="true"
    TrackPosition="true"
    Width="10"
/>

The result is shown in Figure 8-4.

Image from book
Figure 8-4

RoundedCorners

The RoundedCornersExtender pads an existing element to achieve a rounding of the corners that you specify. The Corners property can be set to one of the following:

  • None

  • TopLeft

  • TopRight

  • BottomRight

  • BottomLeft

  • Top

  • Bottom

  • Right

  • Left

  • All

You can set the Radius property to a value other than its default value of 5, and you must set the TargetControlID. Listing 8-8 (RoundedCorners.aspx) demonstrates using the RoundedCornersExtender to shape a panel.

Listing 8-8
Image from book
<%@ Page Language="C#" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Rounded Corners</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager" />
    <div>
    <asp:Panel ID="Panel1" runat="server"
     style="text-indent5px;width:200px;height:100px;background-color:#aaccee">
    &nbsp;Rounded corners are better.
    </asp:Panel>
    <ajaxToolkit:RoundedCornersExtender ID="RoundedCornersExtender1" runat="server"
        TargetControlID="panel1"
        Corners="All"
        Radius="15"
    />
    </div>
    </form>
</body>
</html>
Image from book


Pop-up Controls

One of the things that has historically been different about web applications and desktop applications is dialogs that pop up or are modal. Of course, we have all seen the annoying pop-ups that some applications use in excess. It’s easy to launch another browser instance and show it, but this is obviously not the same as providing a transient piece of UI that is integrated into the application.

Calendar

Selecting a date is a common requirement of many applications. The CalendarExtender attaches to a textbox and pops up a calendar for selecting a date. By default, the calendar is shown when the textbox gets focus, but if you set the PopupButtonID to the ID of another control, the calendar will become visible when that control is clicked.

The CalendarExtender is very easy to use with just a few key properties. The TargetControlID points to the textbox that gets the selected date. The Format property specifies the string format for the date input of the textbox. Listing 8-9 (Calendar.aspx) shows the basic CalendarExtender usage.

Listing 8-9
Image from book
<%@ Page Language="C#" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Calendar Extender</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager" />
    <div>
    <asp:TextBox runat="server" ID="textbox1" />
    <ajaxToolkit:CalendarExtender runat="server" ID="calendarExtender1"
TargetControlID="textbox1" />
    </div>
    </form>
</body>
</html>
Image from book

You can see in Figure 8-5 how a calendar becomes visible and the selected value is automatically written into the textbox.

Image from book
Figure 8-5

ConfirmButton

The ConfirmButtonExtender is slick. I believe slick is the technical term. You set the TargetControlID to a button control or a control that derives from a button. When the button is clicked, the ConfirmText is shown along with OK and Cancel buttons. If the user clicks the OK button, the button fires normally. If not, the button click is canceled. Listing 8-10 (ConfirmButton.aspx) is a page that updates a label with the time on the server each time the button is clicked but only if the ConfirmButtonExtender allows the submit to occur.

Listing 8-10
Image from book
<%@ Page Language="C#" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<script runat="server">
    protected override void OnLoad(EventArgs e) {
        base.OnLoad(e);
        theLabel.Text = DateTime.Now.ToLongTimeString();
    }
</script>
<head runat="server">
    <title>Confirm Button</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager" />
    <div>
    <asp:Label runat="server" ID="theLabel" />
    <asp:Button runat="server" ID="button1" Text="Submit" />
    <ajaxToolkit:ConfirmButtonExtender runat="server"
            ID="cfe1"
            ConfirmText="Are You Sure?"
            TargetControlID="button1" />
    </div>
    </form>
</body>
</html>
Image from book

HoverMenu

The HoverMenuExtender is used to display what is designated by the PopupControlID when the user hovers over the control you specify with the TargetControlID property. This is an easy way to add hover behavior using familiar ASP.NET controls to existing applications or as you develop new applications. The PopupPosition property is used to control where the hover control should be displayed. The options are:

  • Left

  • Right

  • Top

  • Bottom

  • Center

The OffsetX and OffsetY properties are the delta from the control’s position where the hover control should be displayed. Negative values are allowed. You can also specify the position using the PopupPosition property.

    <asp:Label runat="server" ID="theLabel" Text="hover over me" />
    <asp:Label runat="server" ID="popupLabel" Text="hover">the Hover
Menu</asp:Label>
    </div>
    <ajaxToolkit:HoverMenu
        runat="server"
        ID="HoverExtender1"
        TargetControlID="theLabel"
        HoverDelay="25"
        UnhoverDelay="25"
        PopupControlID="popupLabel" />

PopupControl

The PopupControlExtender is the general-purpose way to reveal content based on user action. You specify the TargetControlID that, when selected by the user, should activate the pop-up, along with the ID of the control that should be shown using the PopupControlID. You can also specify the PopupPosition or OffsetX and OffsetY properties. In this example, when the user selects the textbox, a radio button list is shown. I have included explicit script that populates the textbox and hides the pop-up panel when a value is selected.

<asp:Panel runat="server" ID="thePanel" >
<asp:RadioButtonList runat="server" ID="rbl" >
<asp:ListItem Text="Phish" Value="Phish"
onClick="$find('popupBehavior').
<asp:ListItem Text="Daft Punk" Value="Daft Punk"
onClick="$find('popupBehavior').
<asp:ListItem Text="Irina" Value="Irina"
onClick="$find('popupBehavior').
</asp:RadioButtonList>
</asp:Panel>
<div>
<asp:TextBox runat="server" ID="textbox1" />
<ajaxToolkit:PopupControlExtender runat="server" ID="pce"
        TargetControlID="textbox1"
        PopupControlID="thePanel"
        BehaviorID="popupBehavior"
        CommitProperty="value"
    />

ModalPopup

The ModalPopupExtender prevents the user from interacting with the underlying page until a modal dialog has been addressed by the user. It is very similar to the HoverMenuExtender, except that the user must work with the control designated by the PopupControlID before he can proceed. It has properties for specifying the OkControlID and the CancelControlID, along with OnOkScript and

<asp:Button runat="server" ID="theButton" Text="Go" />
<asp:Panel runat="server" ID="popupPanel" Style="display:none" >
<asp:Button runat="server" ID="okButton" Text="ok" /><br />
<asp:Button runat="server" ID="cancelButton" Text="cancel" />
</asp:Panel>
</div>
<ajaxToolkit:ModalPopupExtender
    runat="server"
    TargetControlID="theButton"
    ID="ModalPopupExtender1"
    PopupControlID="popupPanel"
    BackgroundCssClass="modalFilter"
    OkControlID="okButton"
    CancelControlID="cancelButton"
     />

When the button identified by the TargetControlID is selected, the underlying page takes on the BackgroundCssClass, and the Panel designated as the PopupControlID is shown.

CascadingDropDown

The available options for one DropDownList can be a function of the selection made in another DropDownList. The CascadingDropDownExtender makes it easy to enable this in your application. You set the TargetControlID to the DropDownList that should be populated by a call back to the server. You also assign a category to classify the DropDownList.

Before the DropDownList is populated, the PromptText is shown. And while the call to the server is underway, the LoadingText is displayed. You can set the ServicePath property to call a ServiceMethod on a separate web service, or you can just set the ServiceMethod name to a static ScriptMethod located directly in the page, as in Listing 8-11 (CascadingDropDown.aspx).

The first DropDownList lets the user pick a state. In this example, I have included only Washington and Oregon. Once a state is selected, a second DropDownList is populated based on the value selected by the user in the first DropDownList. The way to specify that one DropDownList is dependent on the value of another is to set the ParentControlID of the CascadingDropDownExtender.

Listing 8-11
Image from book
<%@ Import Namespace="System.Web.Services" %>
<%@ Register
    Assembly="AjaxControlToolkit"
    Namespace="AjaxControlToolkit"
    TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<script runat="server" language="C#">
[WebMethod]
[System.Web.Script.Services.ScriptMethod]
public static CascadingDropDownNameValue[]
       GetStates(string knownCategoryValues, string category) {
    return new CascadingDropDownNameValue[] {
        new CascadingDropDownNameValue("Washington", "Washington"),
        new CascadingDropDownNameValue("Oregon", "Oregon") };

}

[WebMethod]
[System.Web.Script.Services.ScriptMethod]
public static CascadingDropDownNameValue[]
       GetCounties(string knownCategoryValues, string category) {
    if(knownCategoryValues.Contains("Washington")) {
        return new CascadingDropDownNameValue[] {
            new CascadingDropDownNameValue("Adams", "Adams"),
            new CascadingDropDownNameValue("Asotin", "Asotin"),
            new CascadingDropDownNameValue("Benton", "Benton"),
            new CascadingDropDownNameValue("Chelan", "Chelan"),
            new CascadingDropDownNameValue("Clallam", "Clallam") };
    }
    else if (knownCategoryValues.Contains("Oregon")) {
        return new CascadingDropDownNameValue[] {
            new CascadingDropDownNameValue("Baker", "Baker"),
            new CascadingDropDownNameValue("Benton", "Benton"),
            new CascadingDropDownNameValue("Clackamas", "Clackamas"),
            new CascadingDropDownNameValue("Clatsop", "Clatsop"),
            new CascadingDropDownNameValue("Columbia", "Columbia") };
    }
    else {
        return null;
    }
}

</script>
    <title>CascadingDropDown</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="scriptManager" />
    <div>
    <asp:DropDownList runat="server" ID="ddl1" Width="200" /><br />
    <asp:DropDownList runat="server" ID="ddl2" Width="200" /><br />
    <ajaxToolkit:CascadingDropDown runat="server" ID="cdd1"
        TargetControlID="ddl1"
        PromptText="Select a State"
        Category="state"
        LoadingText="[Loading States]"
        ServiceMethod="GetStates"
    />

    <ajaxToolkit:CascadingDropDown runat="server" ID="cdd2"
        TargetControlID="ddl2"
        ParentControlID="ddl1"
        PromptText="Select County"
        Category="county"
        LoadingText="[Loading Counties]"
        ServiceMethod="GetCounties"
    />
    </div>
    </form>
</body>
</html>
Image from book


Summary

The ASP.NET AJAX Toolkit makes it easy to add rich animations and interactivity to a web application. In addition to being able to use the UpdatePanel control to enable asynchronous updates of page content, you can use the UpdatePanelAnimation to show the user that background processing is occurring. The Toolkit helps blur the distinction between desktop applications and web applications. Modal dialogs and pop-up dialogs start to push the web application beyond what the user expects from working in the browser.


Chapter 9: Testing and Debugging ASP.NET AJAX Applications

Microsoft’s ASP.NET AJAX framework provides a solid foundation for building efficient and high performance web-based applications that can enhance the overall end user experience. No matter how good a development platform is, however, bugs and other issues can be introduced by developers and triggered by end users. Knowing how to quickly debug ASP.NET AJAX applications can greatly increase your productivity as a developer and reduce the amount of frustration experienced while tracking down issues.

In this chapter you’ll be introduced to debug functionality available in the ASP.NET AJAX script library that can be used to make assertions and perform tracing operations. You’ll also learn about several different debugging techniques available in Visual Studio .NET 2005, Internet Explorer, and Firefox that can simplify the process of debugging ASP.NET AJAX applications. Finally, you’ll learn how to use different tools to intercept and view request and response messages, to more easily track down data issues and monitor AJAX request and response message sizes.

Debug and Release Scripts

ASP.NET AJAX ships with debug and release versions of client script files that drive functionality in AJAX-enabled pages. These scripts are installed at C:\Program Files\Microsoft ASP.NET\ ASP.NET 2.0 AJAX Extensions\[Version]\MicrosoftAJAXLibrary\System.Web .Extensions\[Version] on the web server. The debug scripts are placed in a folder named Debug, while the release scripts are placed in a folder named Release. Understanding how to switch between these different script versions can simplify the process of debugging ASP.NET AJAX applications and make it easier to step through Microsoft’s client script files as well as any custom script files you may write.

To tell the ASP.NET AJAX framework which version of the library scripts you want to use, you have to set the ScriptManager’s ScriptMode property to one of four values defined in a ScriptMode enumeration, as shown in the following table.

Open table as spreadsheet
Member | Description
Auto | The client script files to use are determined at runtime based upon configuration settings in web.config and machine.config. If the deployment element’s retail attribute is set to false (the default) and the page element’s debug attribute is set to true, debug scripts are used. Otherwise, release scripts are used.
Inherit | When applied directly to the ScriptManager control, it is the same as using Auto. When applied to a ScriptReference object (nested in a ScriptManager control), the settings are inherited from the ScriptManager control’s ScriptMode property.
Debug | Debug versions of the client script files are loaded into an AJAX-enabled page and used by the framework. This setting is overridden if the deployment element’s retail attribute is set to true in machine.config, in which case release scripts are used.
Release | Release versions of the client script files are loaded into an AJAX-enabled page and used by the framework.

If you don’t specify a value for the ScriptManager’s ScriptMode property, it defaults to Auto. In this case, the control looks at the compilation element’s debug attribute value in web.config as well as the deployment element’s retail attribute value in machine.config. The following example shows how to define the compilation element and associated debug attribute in web.config.

<system.web>
    <!-- Debug scripts will be used when ScriptMode is Auto -->
    <compilation debug="true" />
</system.web>
Tip

The deployment element is optional and can be defined in machine.config. Its retail attribute has a default value of false. The retail attribute can be set to true to indicate that this is a production server. The purpose of this attribute is to ensure that you won’t use the Debug mode in production by mistake. A common problem is that many people forget to disable debugging in web.config when making a production deployment, so this setting will override web.config. Once the retail attribute is set to true, trace output, custom error, and debug configuration settings are disabled on the server, but runtime performance is increased. The discussion that follows assumes that the retail attribute is set to the default value of false.

By letting the ScriptManager controls within a website default to Auto, you can easily control which client scripts are loaded by simply changing the debug attribute in web.config. When the ScriptMode property is set to Auto and the debug attribute in web.config is set to true, debug versions of client scripts will be loaded and used. When debug is set to false, release versions of the client script files will be loaded and used.

You can force release scripts to be loaded even when the debug attribute is set to true in web.config by changing the ScriptMode to Release, as shown in Listing 9-1.

Listing 9-1
Image from book

<asp:ScriptManager ID="ScriptManager1" runat="server" ScriptMode="Release">
      <scripts>
         <asp:ScriptReference Path="~/Scripts/Album.js" />
      </scripts>
</asp:ScriptManager>
Image from book

Conversely, debug scripts can be loaded regardless of the debug value in web.config by setting the ScriptMode to Debug, as shown in Listing 9-2.

Listing 9-2
Image from book

<asp:ScriptManager ID="ScriptManager1" runat="server" ScriptMode="Debug">
      <scripts>
         <asp:ScriptReference Path="~/Scripts/Album.js" />
      </scripts>
</asp:ScriptManager>
Image from book

Because the debug scripts in the ASP.NET AJAX Library have nicely formatted code that is easy to read and step through, you’ll want to set the ScriptMode to Debug (or Auto and change the debug attribute in web.config to true) while working through application problems. Although you can use the release client scripts during development, they have all formatting stripped out and are more difficult to use for debugging purposes.

In addition to loading ASP.NET AJAX client script files, the ScriptManager control can be used to load custom script files. This is done using the ScriptManager’s Scripts property, as shown in Listing 9-3. Custom scripts are loaded by adding a ScriptReference component within a Scripts tag inside of the ScriptManager control. ScriptReference defines the path to the script as well as the ScriptMode. One key advantage of loading script code this way is to let you load different scripts depending on whether the application is running in Debug mode or not.

Listing 9-3
Image from book

<asp:ScriptManager ID="ScriptManager1" runat="server">
      <Scripts>
         <asp:ScriptReference Path="~/Scripts/Album.js" ScriptMode="Debug" />
      </Scripts>
</asp:ScriptManager>
Image from book

If you’re loading custom scripts using the ScriptReference component, you must notify the ScriptManager when the script has finished loading by adding the following code at the bottom of the script:

if(typeof(Sys)!=='undefined') Sys.Application.notifyScriptLoaded ();

Because the ScriptMode attribute is set to a value of Debug in Listing 9-3, the ScriptManager control will automatically look for a file named Album.debug.js instead of Album.js. If the file is not found, an error will be raised.

Having both release and debug versions of a script can be useful in some situations. For example, the release version of the script may have all whitespace stripped out of it to minimize its size, while the debug script may be formatted nicely to make it easy to step through; plus, it may contain additional debugging statements to help you isolate problems. Microsoft follows this practice with the client scripts used in the ASP.NET AJAX Library.

Tip

Several different tools can be used to compress JavaScript files through removing whitespace and comments and sometimes even shortening variable names. Some examples of JavaScript compressors can be found at http://dynamic-tools.net/toolbox/javascript_compressor and http://www.codeproject.com/csharp/jscompress.asp.

When the ScriptMode property is set to Inherit on the ScriptReference component, the correct version of the custom script to load (debug or release) will be determined by the value defined on the ScriptManager’s ScriptMode property. In other words, the ScriptReference’s ScriptMode value will be inherited from the ScriptManager’s ScriptMode. Listing 9-4 shows how ScriptMode settings can be inherited from the ScriptManager.

Listing 9-4
Image from book

<asp:ScriptManager ID="ScriptManager1" runat="server" ScriptMode="Debug">
      <Scripts>
         <asp:ScriptReference Path="~/Scripts/Album.js" ScriptMode="Inherit" />
      </Scripts>
</asp:ScriptManager>
Image from book

Now that you’ve seen how debug and release client scripts can be loaded, let’s examine the ASP.NET AJAX Library classes that can be used in various debugging situations.


Using the Error Class

The ASP.NET AJAX Library includes several useful classes with respect to debugging and testing, including the Error and Sys.Debug classes. The Error class extends the native JavaScript Error object and allows more specific error objects and messages to be created for a given error. It is used extensively throughout the ASP.NET AJAX Library debug scripts to test that the correct number of parameters are passed to functions, passed parameter types match up with the expected types, types aren’t undefined, functions are fully implemented, and much more. The Error class is used sparingly in the release version of the ASP.NET AJAX Library scripts so that file sizes are minimized as much as possible.

The Error class has several different functions that can be used to create specific types of errors. The majority of the functions that can be called to create error objects relate specifically to function parameters, but others exist that are used by classes such as the PageRequestManager class. All of the Error class’s available functions are shown in the following table.

Open table as spreadsheet

Function Name

Description

argument(paramName, message)

Used to create a Sys.ArgumentException object with the name of the function parameter that caused the exception, as well as the specified error message.

argumentNull(paramName,message)

Used to create a Sys.ArgumentNullException object with the name of the function parameter that caused the exception, as well as the specified error message.

argumentOutOfRange(paramName, actualValue,message)

Used to create a Sys.OutOfRangeArgumentException object. It accepts three parameters, including the parameter name, the actual value of the parameter, and the error message.

argumentType(paramName,actualType,expectedType,message)

Used to create a Sys.ArgumentTypeException object. Can be used when a parameter passed to a function is not the proper type. It accepts four parameters, including the parameter name, actual type passed for the parameter, the expected type of the parameter, and the error message.

argumentUndefined(paramName,message)

Used to create a Sys.ArgumentUndefinedException object with the name of the function parameter that caused the exception as well as the specified error message.

create(message,errorInfo)

Used to create a new instance of an Error object with the specified message and extended information about the error. The errorInfo object passed must contain a name field containing a string that identifies the error. An example of creating an errorInfo parameter is:

Error.create(a, {name:"Sys
.ArgumentTypeException",
  paramName:d,actualType:c,expectedType:b})

invalidOperation(message)

Used to create a Sys.InvalidOperationException with the specified error message.

notImplemented(message)

Used to create a Sys.NotImplementedException object with the specified error message.

parameterCount(message)

Used to create a Sys.ParameterCountException object with the specified error message. Used to validate the number of parameters passed to a function.

popStackFrame()

Used to update the fileName and lineNumber fields of the Error object instance to indicate where the Error was thrown. This solves problems in browsers that assign values to the fileName and lineNumber fields based upon where the Error object was created rather than where it was thrown.

You can use the Error object to raise specific types of errors when invalid parameters are passed to a function, when calls are made to functions that aren’t fully implemented, or when you need to ensure that the correct line number and filename show up when errors are raised.

Listing 9-5 shows an example of using the Error object’s argumentType, popStackFrame, and notImplemented functions with a custom script that defines an Album class.

Listing 9-5
Image from book

Wrox.ASPAJAX.Samples.Album.prototype = {

    //Additional code removed for brevity

    addSong: function(song)
    {
        /// <value type="Wrox.ASPAJAX.Samples.Song"></value>
        if (Object.getTypeName(song) != 'Wrox.ASPAJAX.Samples.
        {
            var e = Error.argumentType('song',
              Wrox.ASPAJAX.Samples.Song,"Wrox.ASPAJAX.Samples.Song required!");
            e.popStackFrame();
            throw e;
        }
        Array.add(this._songs,song);
    },
    rateSong: function(song,rating)
    {
        throw Error.notImplemented("rateSong() has not yet been implemented");
    }

    //Additional code removed for brevity

}
Image from book

The first use of the Error object is in the definition for the addSong() function in Listing 9-5. This function accepts a Wrox.ASPAJAX.Samples.Song object as a parameter. If a Song parameter type is not passed, a call is made to the Error.argumentType function to create a Sys.ArgumentTypeException error object. This function accepts the name of the parameter in error (song in this example), the actual parameter type passed, the expected parameter type, and the error message that should be shown to the caller. Once the Error object is created, a call is made to popStackFrame to ensure that the line number and filename are properly reported to the caller. In some browsers, the line number is reported at the location that the error was created rather than the location where it was actually thrown. The popStackFrame function remedies this problem and ensures that the line number and filename are reported correctly.

The Error object is also used in the definition for the rateSong function in Listing 9-5. Because this function is not yet implemented, a call is made to the Error.notImplemented function, and the error message that the caller should see is passed as a parameter. Listing 9-6 shows calls to the Album class’s addSong and rateSong methods and demonstrates how to catch returned errors.

Listing 9-6
Image from book

function RateSong()
{
    var album = new Wrox.ASPAJAX.Samples.Album();
    var song = new Wrox.ASPAJAX.Samples.Song(3,$get("txtSong"));
    try
    {
        //Should pass song object here but demonstrate using Error object in Album
        //to verify proper parameter type is passed
        album.addSong(album);
    }
    catch(e)
    {
        alert(e.message);
    }
    try
    {
        //rateSong() not implemented
        album.rateSong(song,$get("txtSongRating"));
        alert("Song rated!");
    }
    catch(e)
    {
        alert(e.message);
    }
  }
Image from book

Now that you’ve seen how the ASP.NET AJAX Library’s Error class can be used, let’s take a look at the Sys.Debug class and see how it can be used to write error messages and identify the reason they occurred.


Using the Sys.Debug Class

The ASP.NET AJAX Library includes a helper class named Sys.Debug that can be used on the client side to write to trace logs, perform assert statements, and trigger a debug session in Visual Studio .NET. Knowing how to use the Sys.Debug class can help simplify the process of ensuring that client-side functionality is performing properly and allow you to more easily view data passed from function to function in an application.

The Sys.Debug class is included in the release and debug versions of the MicrosoftAjax.js client- script file shipped with ASP.NET AJAX. It defines several different functions that can be called to perform assertions, tracing, and debugging. The following table shows the different functions available on the Sys.Debug class.

Open table as spreadsheet
Function Name | Description
assert(condition,message,displayCaller) | Asserts that the condition parameter is true. If the condition being tested is false, a message box will be used to display the message parameter value. If the displayCaller parameter is true, the method also displays information about the caller.
clearTrace() | Erases statements output from tracing operations.
fail(message) | Causes the program to stop execution and break into the debugger. The message parameter can be used to provide a reason for the failure.
trace(message) | Writes the message parameter to the trace output.
traceDump(object,name) | Outputs an object’s data in a readable format. The name parameter can be used to provide a label for the trace dump. Any subobjects within the object being dumped will be written out by default.

Performing Trace Operations

If you’ve used ASP.NET’s built-in trace functionality, you’ll feel right at home with the Sys.Debug class’s tracing capabilities. It can be used to write client-side trace messages that can be used to track the flow of an ASP.NET AJAX application. An example of using the Sys.Debug class to write to the client-side trace output is shown in Listing 9-7. Figure 9-1 shows what the trace log looks like when rendered to the page.

Listing 9-7
Image from book

var album = new Wrox.ASPAJAX.Samples.Album();
album.set_title("Sam's
album.set_artist("The Killers");

Sys.Debug.trace("Set album title and artist properties");

$get("lblTitle").innerHTML = album.get_title();
$get("lblArtist").innerHTML = album.get_artist();

Sys.Debug.trace("Album properties written out to page");
Image from book
Image from book
Figure 9-1

Adding Sys.Debug.trace statements into existing JavaScript code doesn’t automatically cause the trace messages to be written to the page. In fact, at first glance you’ll wonder if any type of tracing operation was performed at all when viewing a page that contains one or more trace statements. To view trace output in the page, you’ll need to add a TextArea tag into the page and give it an id of TraceConsole. The trace function looks for the TraceConsole ID defined on a TextArea tag as it writes out trace messages. Listing 9-8 shows code from the ASP.NET AJAX Library that handles writing trace statements.

Listing 9-8
Image from book

function Sys$_Debug$_appendTrace(text) {
    var traceElement = Sys.UI.DomElement.
    if (traceElement && (traceElement.tagName.toUpperCase() ===
        traceElement.value += text + '\n';
    }
}
Image from book

Here’s an example of defining a TextArea tag to capture and display trace information:

<textarea id="TraceConsole" rows="25" cols="60"></textarea>
<textareaid="TraceConsole"rows="25"cols="60"></textarea>

You may look at this and wonder if there may be a better way to view trace output. After all, once an application is ready to move to production, it would be time consuming (and error prone) to locate all of the pages that have TextArea tags used to display trace statements and then remove them. Fortunately, Visual Studio .NET can also be used to view trace output using the Output window, as shown in Figure 9-2. This won’t require you to use a TraceConsole TextArea on your page.

Image from book
Figure 9-2

Additional information about using Visual Studio .NET to debug ASP.NET AJAX applications and view other useful information is covered later in this chapter.

In addition to allowing custom trace data to be written to the page, the Sys.Debug class’s traceDump function can be used to convert JSON objects into a more readable format to easily inspect the data within an object. This will show all the properties of the object without requiring you to specify them individually. The traceDump function accepts the object to display as a parameter along with a label to associate with the trace dump to make it easier to identify. Listing 9-9 shows an example of using the traceDump function to write the values contained within an Album object. Figure 9-3 shows the output.

Listing 9-9
Image from book

var album = new Wrox.ASPAJAX.Samples.Album();
album.set_title("Sam's Town");
album.set_artist("The Killers");
Sys.Debug.trace("Set album title and artist properties");

$get("lblTitle").innerHTML = album.get_title();
$get("lblArtist").innerHTML = album.get_artist();

Sys.Debug.trace("Album properties written out to page");

//Dump the album object to the trace output console
Sys.Debug.traceDump(album,"Album Details:");
Image from book
Image from book
Figure 9-3

The Sys.Debug.traceDump function also handles writing nested subobjects automatically. For example, Listing 9-10 shows how an Album object containing an array of nested Song objects is written to the page. Looking at the output generated from this call (see Figure 9-4), you’ll see that it is fairly verbose. In cases where you would like to clear the current trace log so that other trace statements are easier to find in the trace console area, you can use the Sys.Debug.clearTrace function.

Listing 9-10
Image from book

var album = new Wrox.ASPAJAX.Samples.Album();
album.set_title("Sam's Town");
album.set_artist("The Killers");

//Add Song objects into Album's
album.addSong(new Wrox.ASPAJAX.Samples.Song(3,"When you were young"));
album.addSong(new Wrox.ASPAJAX.Samples.Song(7,"Uncle Johnny"));

$get("lblTitle").innerHTML = album.get_title();
$get("lblArtist").innerHTML = album.get_artist();
Sys.Debug.trace("Album properties written out to page");

//Write out Album and nested Song objects
Sys.Debug.traceDump(album,"Album and Song Details:");
Image from book
Image from book
Figure 9-4

Performing Assert Operations

The Sys.Debug class can also be used to assert that a condition is true in order to check that objects are available to call or that an action has occurred as expected. In cases where the condition fails, a message box will be displayed. The assert function accepts three parameters, including the condition to check, the message to display if the assertion fails, and whether or not the caller’s information should be displayed.

Using the assert function makes it straightforward to test a specific condition within JavaScript code and alert you when it doesn’t evaluate to true. It’s a nice feature, as it doesn’t require you to add conditional statements (if or switch statements) into an application.

An example of using the Sys.Debug.assert function to check that an Album contains songs is shown in Listing 9-11, while an example of the output is shown in Figure 9-5.

Listing 9-11
Image from book

var album = new Wrox.ASPAJAX.Samples.Album();
album.set_title("Sam's Town");
album.set_artist("The Killers");

//Assert that the songs.length condition is true or not
Sys.Debug.assert(album.get_songs.length > 0,"No songs for album");
Image from book
Image from book
Figure 9-5

You can also obtain additional details about the condition being evaluated by passing a third Boolean parameter named displayCaller to the assert function as shown next:

debug.assert(album.get_songs.length > 0,"No songs for album",true);

Figure 9-6 shows what the assert message box looks like when the displayCaller parameter is set to true.

Image from book
Figure 9-6

When an assertion fails, you are given the option to break into a debugger to more thoroughly evaluate the condition and step through the code. A debug session can also be started by calling the Sys.Debug.fail function within JavaScript code. For debugging to start and work properly, however, specific settings within Internet Explorer must be enabled. In the next section, you’ll see how to enable Internet Explorer for debugging and work with Visual Studio .NET and Microsoft’s Script Debugger.


Enabling Internet Explorer for Debugging

Debugging JavaScript has proven to be somewhat of a challenge and has resulted in many developers resorting to “alert style” debugging to better understand how an application is working. Fortunately, Internet Explorer 6 or higher includes integrated debugging functionality that can be used to start a debug session and step through ASP.NET AJAX code with a debugging tool such as Visual Studio .NET 2005 or Microsoft’s Script Debugger, as shown in the following sections. Learning how to leverage

Internet Explorer debugging features can enhance your productivity and significantly minimize the amount of time you spend hunting down bugs and other issues. The debug capabilities in Internet Explorer 6 or higher are disabled by default but can be turned on by going to Tools Image from book Internet Options Image from book Advanced. The Advanced tab defines two items that need to be unchecked (a check means you can’t debug):

  • Disable script debugging (Internet Explorer)

  • Disable script debugging (Other)

Also, check the “Display a notification about every script error” check box below these items so that you’re notified as errors occur in a page. If this box isn’t checked, an icon will appear in the lower-left corner of the browser; unless you click, it you may miss noting that a client-side error occurred. A side-effect of checking this check box is that you’ll find that many other websites have errors as you browse to them, so you may want to leave it unchecked when you’re not debugging your own applications.

Figure 9-7 shows what the Internet Explorer Advanced settings should look like to enable debugging.

Image from book
Figure 9-7

Once debugging is enabled in Internet Explorer, an ASP.NET AJAX page can be debugged using several techniques. The following sections walk you through the options.

Debugging with Internet Explorer and Visual Studio .NET 2005

You can start a debug session several different ways. One of the easiest ways is to use the built-in script debugging capabilities of Internet Explorer. To start a debug session for an ASP.NET AJAX page, navigate to the page that you’d like to debug and select View Image from book Script Debugger from the Internet Explorer menu (see Figure 9-8). For this to work, you must enable Internet Explorer for debugging as described in the previous section.

Image from book
Figure 9-8

You’ll be presented with two different options, Open and Break at Next Statement, as shown in Figure 9-8. If you select Open, you’ll be prompted to start using Visual Studio .NET as the debugger, as shown in Figure 9-9. If Visual Studio .NET is already open, you can begin debugging, or you can choose to start a new instance of Visual Studio .NET to use for debugging. If you select Break at Next Statement, nothing will happen until an action is performed that causes script within the page (or script referenced by the page) to execute. Once script code is executed in Internet Explorer, you’ll be prompted to use the Visual Studio .NET Just-In-Time debugger.

Image from book
Figure 9-9

Once Visual Studio .NET is open and available for debugging, you won’t be able set a breakpoint if the script is embedded within the page. Instead, you’ll see a message at the bottom of the Visual Studio .NET status bar that states “This is not a valid location for a breakpoint” when you try to set the breakpoint. There are several ways to solve this problem. First, you can add the JavaScript “debugger” statement directly into the code where you’d like to start a debug session, which will force a breakpoint to occur at runtime at that location in your code. However, ASP.NET AJAX’s Sys.Debug class (discussed in the previous section) is designed for this purpose and is a more appropriate choice for debugging AJAX applications. You can call the Sys.Debug class’s fail function to trigger the debugger when a specific line of code is hit.

The Sys.Debug.fail function accepts only one parameter that represents the reason for the failure. Listing 9-12 shows an example of using the Sys.Debug.fail function to break into a debug session right before songs are added into an Album object.

Listing 9-12
Image from book

var album = new Wrox.ASPAJAX.Samples.Album();
album.set_title("Sam's Town");
album.set_artist("The Killers");

//Force the debugger to appear
Sys.Debug.fail("Debug song additions");

album.addSong(new Wrox.ASPAJAX.Samples.Song(3,"When you were young"));
album.addSong(new Wrox.ASPAJAX.Samples.Song(7,"Uncle Johnny"));
$get("lblTitle").innerHTML = album.get_title();
$get("lblArtist").innerHTML = album.get_artist();
Image from book

It’s helpful to call Sys.Debug.fail to trigger a debug session when a line of JavaScript code is executed, but you must remember to remove all these calls before moving an application to a production environment, because it will always trigger the debugger when debugging is enabled in Internet Explorer. You can have two versions of your script (Microsoft does this with its debug and release scripts) and set the appropriate ScriptMode on the ScriptManager to determine which script is loaded at runtime, as discussed at the beginning of this chapter. Although this means that your code is duplicated, the release script can have all whitespace characters stripped out of it to minimize its size, while the debug script can provide an easy-to-read script that contains the necessary calls to the Sys.Debug class for debugging, tracing, assertions and other operations.

Another option for triggering the debugger when the script is embedded in the page is to move all of the JavaScript code in the page into its own script file, instead of including it directly in the .aspx page. In this case, a separate file with an extension of .js is referenced by the page to be loaded separately at runtime, instead of being sent down to the browser along with the HTML markup. After you’ve done this you can set a breakpoint in the external script file. Figure 9-10 shows an example of JavaScript in a file named Listing9-12.js that has a breakpoint successfully set.

Image from book
Figure 9-10

As you step through the code by pressing F11 (step into) or F10 (step over), you may be prompted to select the location of dynamically loaded script files needed by ASP.NET AJAX, or you may encounter an error that states “There is no source code available for the current location.” This occurs when you try to step into dynamically generated script code that was loaded using the ScriptResource.axd and WebResource.axd HTTP handlers, so in this case, there isn’t a separate physical file that Visual Studio can walk through. Although this problem can be frustrating at first, a simple tool that’s already part of Visual Studio .NET can alleviate this problem.

Tip

Visual Studio .NET 2005 SP1 helps resolve this issue: however, knowing how to deal with the error if it arises is still helpful.

Visual Studio .NET 2005 includes a very functional tool called the Script Explorer that can be used to navigate through scripts within a page. The Script Explorer can be used to load dynamic scripts in the editor so that you can step into them easily using the debugger and avoid the dreaded “There is no source code available for the current location” error. If you are prompted to load a script file, simply cancel the dialog box (or if you receive the aforementioned error, click OK). Select Debug Image from book Windows Image from book Script Explorer (or press Ctrl+Alt+N) to view the Script Explorer window. Double-click the page currently being debugged to open it in the editor, and then double-click all of the other script files shown. This will open all of the files referenced by the page in Visual Studio .NET so that the debugger can access the necessary source code to allow for a rich debugging experience.

Figure 9-11 shows an example of the Script Explorer and the script files used in an ASP.NET AJAX-enabled page named Listing9-12.aspx.

Image from book
Figure 9-11
Tip

As you step through code during a debug session, you may notice that as you mouse over JavaScript variables you don’t see any information or data as you do when debugging VB.NET or C# code. While you can typically use the Autos, Locals, Watch, or Immediate windows to access the variable data, you can also try highlighting the variable with the cursor and then hovering over the highlighted text with the cursor to see the data contained within the variable. This trick doesn’t work in every situation but is a good one to keep in mind.

You can also start a debug session directly in Visual Studio .NET 2005 much as you would when debugging VB.NET or C# code within an ASP.NET page. First, right-click the page you’d like to debug in the Solution Explorer and select Set As Start Page from the menu. Then press F5 or click the green play but-ton (the button with the green arrow on it) on the debug toolbar to start Internet Explorer and display the page. Set your breakpoints in the separate script files and then trigger the breakpoints by performing the action you’d like to debug in the browser.

As you step through code, you have full access to standard debug windows such as the Autos, Locals, Watch, and Immediate as shown in Figure 9-12. By using these windows, you can quickly access variable data and drill down into your applications to see what is happening internally.

Image from book
Figure 9-12
Tip

Although this section focuses on debugging features in Visual Studio .NET 2005, Visual Web Developer 2005 Express also has integrated debugging features that can be used to debug ASP.NET AJAX applications. While not as full-featured as the Visual Studio .NET 2005 debugger, it does have support for the Script Explorer, Locals, and Watch windows as well as several others.


Debugging with Internet Explorer and the Microsoft Script Debugger

In cases where you don’t have access to Visual Studio .NET 2005 but need to debug ASP.NET AJAX pages and associated scripts, the Microsoft Script Debugger can be used to view and debug scripts and step through code line by line. The Script Debugger has been around for several years as a stand-alone product that can run on a variety of Microsoft operating systems, including Windows NT 4, Windows 2000, Windows Server 2003, Windows XP, and Windows Vista. It can be downloaded from http:// www.microsoft.com/downloads/details.aspx?FamilyID=&DisplayLang=en (this URL is, of course, subject to change).

Once installed, the Script Debugger is automatically triggered when you select View Image from book Script Debugger Image from book Open (or Break at Next Statement) from the Internet Explorer menu. While the Script Debugger isn’t as robust as the debugger built into Visual Studio .NET 2005, it is very functional and can help identify problems encountered in ASP.NET AJAX applications. Figure 9-13 shows what the Script Debugger looks like in action.

Image from book
Figure 9-13

You can view all of the scripts used in the page through the Running Documents window and set and remove breakpoints by placing the cursor on the line where you’d like to set (or remove) a breakpoint and then selecting the appropriate icon on the toolbar. The hand icon sets breakpoints, and the hand with a red “X” over it removes breakpoints. Scripts that are dynamically loaded in the page through ScriptResource.axd and WebResource.axd don’t present a problem and are directly accessible to step into without any extra effort on your part.

Once the Script Debugger is started, you can step through code by pressing F8, step over code by pressing Shift+F8, or step out of code by pressing Ctrl+Shift+F8. Alternatively, you can select the appropriate icons on the toolbar to step through the code. As you step through code, you can view variables and their values by using the Command Window. Type the variable name or object function that you want to call, and it will be displayed.

Figure 9-14 shows an example of the windows available in the Script Debugger.

Image from book
Figure 9-14
Tip

While you can run Visual Studio .NET 2005 and the Microsoft Script Debugger on the same machine, if you uninstall the Script Debugger, you may notice that the Script Debugger menu entry no longer appears in Internet Explorer or that Visual Studio .NET doesn’t handle calls to Sys.Debug.fail. This can be fixed by performing a repair on the Visual Studio .NET installation.


Debugging with Firefox and Firebug

Although Internet Explorer is still the most widely used browser according to statistics available at the time this chapter is being written, Mozilla Firefox has made significant inroads and is being used by more and more people. As a result, you’ll likely need to debug your ASP.NET AJAX applications in Firefox as well as Internet Explorer. Firefox doesn’t offer any direct integration with Visual Studio .NET, unfortunately, but it does have extensions that can be downloaded and installed that make it rather straightforward to debug ASP.NET AJAX applications.

One of the most popular debugging extensions available for Firefox 1.5 or higher is Firebug. You can download Firebug from the extensions library located at https://addons.mozilla.org/ firefox/extensions. Type “Firebug” in the search box, and you should quickly find it and be able to click the Install Now button to add it to your browser extensions.

Once installed, Firebug allows you to view the Document Object Model (DOM) of a page, view CSS details, and perform various debugging operations. You can view a page directly in Firefox from Visual Studio .NET by right-clicking the page in the Solution Explorer and selecting Browse With from the menu. The Browse With dialog box will appear. Click the Add button to view the Add Program dialog box as shown in Figure 9-15, and locate where firefox.exe was installed on your computer. Once the program is added into the list of available browsers, you can select it and hit the Browse button to view any page directly in Firefox instead of in Internet Explorer.

Image from book
Figure 9-15

Once a page is loaded in Firefox, you can access Firebug functionality in several ways. First, you can go to Firefox’s Tools menu and select Firebug Image from book Open Firebug from the menu (or use the Ctrl+Shift+L hotkey). You can also add a call to Sys.Debug.fail in your JavaScript code. When the fail function is called, Firebug will automatically appear at the bottom of the browser and allow you to step through the code line by line. Figure 9-16 shows what Firebug looks like. You’ll notice that it has several different tabs that allow access to scripts, HTML, DOM elements, and CSS.

Image from book
Figure 9-16

It’s quick and easy to get started with Firebug. All scripts loaded in a page are easily accessible without additional effort. When a script is needed by Firebug, its source code will automatically appear in the Script window. Variables within client-script files are accessible through the Console window (which acts much like Visual Studio .NET’s Command window) and in the Script window. HTML code, styles, and other DOM features can be accessed in their respective windows.

Figure 9-17 shows an example of the Script window during a debug session. Notice that several buttons located in the top of the Firebug toolbar are available for stepping into, stepping over, and stepping out of code. Breakpoints can be set by clicking in the grey tray area where the line numbers are shown. As code is being debugged, the value of the variables currently in scope are displayed to the right of the Script window, allowing you to easily access properties and their associated values. For example, Figure 9-17 shows information for a variable named album.

Image from book
Figure 9-17

Firebug makes it easy to walk through client scripts and is also useful for inspecting HTML code. It can also come in handy when you need to look at how CSS styles are being applied to different elements in a page.


Viewing ASP.NET AJAX Request and Response Messages

Once you’ve ironed out all of the bugs in an ASP.NET AJAX Application, you’ll probably want to perform other types of tests to ensure that the application performs as expected. It’s difficult to know how an application truly performs unless you take the time to look at some of the request and response messages being sent from the client to the server. By looking at the raw message data, you may discover ways to minimize the size of the messages and speed up the request/response mechanism. It’s also important to see the actual request and response because this is a 100-percent authoritative view of what communication is actually taking place between the browser and web server; if this appears different from what you intended, it can alert you to bugs.

Several free and easy-to-use tools exist for inspecting request and response messages. The next sections introduce several such tools, including Fiddler and the Web Developer Helper.

Using Fiddler to Inspect Messages

Fiddler is an excellent tool created by Microsoft’s Eric Lawrence that can be used to inspect AJAX messages exchanged between the client and server. It can be downloaded from http://www.fiddler-tool.com and is free to use. It provides a simple and effective way to view many different details about messages, including request and response headers, raw message data, message sizes, and other useful information.

After Fiddler is installed, it can be accessed directly from the Internet Explorer Tools menu or loaded from the Windows Start Image from book Programs menu. During installation, Fiddler registers itself with Internet Explorer by setting itself up as a proxy. Requests for 127.0.0.1 (localhost) are automatically routed to port 8888, which Fiddler listens on. All requests made from the browser will automatically show up in the Fiddler HTTP Sessions list, as shown in Figure 9-18.

Image from book
Figure 9-18

Fiddler can be useful when you want to see the overhead involved with using automated versus manual AJAX updates, such as the difference between using an UpdatePanel and using pure JavaScript or XML Script calls. While the UpdatePanel is easier to use, it typically has a larger message payload than equivalent calls made with JavaScript or XML Script. For example, the code shown in Listing 9-13 uses the UpdatePanel to load customer data into a GridView control based upon a country or a customer ID that the end user types into textboxes. Using Fiddler, it’s easy to see exactly what data is passed to the server as well as what data is passed back in the response message.

Listing 9-13
Image from book

<asp:UpdatePanel ID="udPanel" runat="server">
  <ContentTemplate>
    <asp:GridView ID="gvCustomers" runat="Server" Width="96%"
      GridLines="none" ShowHeader="false" AutoGenerateColumns="false">
        <RowStyle HorizontalAlign="left" />
        <Columns>
            <asp:BoundField ItemStyle-Width="25%" DataField="CustomerID" />
            <asp:BoundField ItemStyle-Width="25%" DataField="ContactName" />
            <asp:BoundField ItemStyle-Width="25%" DataField="CompanyName" />
            <asp:BoundField ItemStyle-Width="25%" DataField="Country" />
        </Columns>
    </asp:GridView>
    <asp:HiddenField ID="hidField" runat="server" />
  </ContentTemplate>
  <Triggers>
    <asp:AsyncPostBackTrigger ControlID="btnSubmit" EventName="Click" />
    <asp:AsyncPostBackTrigger ControlID="btnSubmit2" EventName="Click" />
  </Triggers>
</asp:UpdatePanel>
Image from book

Using Fiddler, you can inspect the messages being sent as the UpdatePanel is triggered and refreshed. Figure 9-19 shows what the different messages look like in Fiddler using the Session Inspector tab. The top pane in the figure shows the request message, while the bottom pane shows the response message. Although the request message is fairly small, you can see that the response message is relatively large. It contains all of the data that will be updated in the GridView control as well as ViewState information and other details. If ViewState is not needed, it can be turned off, which will significantly reduce the size of the UpdatePanel’s response message. In this example, the response message size can be reduced from 4,908 characters to 3,106 by turning off ViewState on the GridView control.

Image from book
Figure 9-19
Tip

If you try to view Listing9-13.aspx using Visual Studio .NET 2005’s built-in Web server, you may notice that the request and response messages do not show up in Fiddler. To see the request and response messages in Fiddler, add a period (.) character immediately after localhost (but before the colon and port number) in the browser. For example, the following URL will show up in Fiddler prop-erly (notice the”.“ after localhost): http://localhost.:49526/Chapter9/Listing9-13.aspx.

The code in Listing 9-14 performs the same task shown earlier with the UpdatePanel but does it using pure JavaScript calls and a ListView control defined using XML Script (part of the ASP.NET AJAX Futures at the time this chapter was written). Running Fiddler to track messages results in a response message size of only 1,730 characters, as shown in Figure 9-20. The request and response messages are significantly smaller because the JSON objects sent to and from the server are much more compact compared to sending HTML, ViewState, and other data.

Listing 9-14
Image from book

function GetCustomerByCountry() {
    var country = document.getElementById("txtCountry").value;
    //Call Web Service
    Wrox.ASPAJAX.Samples.CustomersService.GetCustomersByCountry(country,
      OnWSRequestComplete);
}

function GetCustomerByID() {
    var custID = document.getElementById("txtCustomerID").value;
    Wrox.ASPAJAX.Samples.CustomersService.GetCustomersByID(custID,
      OnWSRequestComplete);
}

function OnWSRequestComplete(results) {
    var listView = $find("searchResults");
    if (listView != null)
    {
        listView.set_data(results);
    }
    GetMap(results);
}
Image from book
Image from book
Figure 9-20

There’s much more to Fiddler than can be discussed in this chapter. However, you’ve seen the fundamentals on how it can be used to view request and response messages so that you can locate data errors and compare techniques of sending request and response messages.

Using Web Development Helper to Inspect Messages

Fiddler isn’t the only tool that can be used to view ASP.NET AJAX request and response messages. Another great tool is the Web Development Helper. Web Development Helper is written by Nikhil Kothari, one of the key ASP.NET architects at Microsoft. It’s an excellent tool that can perform a variety of tasks such as viewing HTTP requests and responses, debugging scripts, and viewing the DOM of a page. You can download the tool at http://projects.nikhilk.net/Projects/ WebDevHelper.aspx.

Once installed, the tool is accessible directly in Internet Explorer by selecting Tools Image from book Web Development Helper from the menu. The tool loads directly into Internet Explorer, which makes it easy to access and use. Figure 9-21 shows what the Web Development Helper tool looks like while running within Internet Explorer.

Image from book
Figure 9-21

To inspect request and response messages you can click the Enable Logging check box on the Web Development Helper toolbar. All messages sent or received using localhost will automatically be logged, along with the status code and response message size. Double-clicking a logged message allows you to see the request and response headers as well as all of the content within the individual messages. Figure 9-22 shows an example of using the Web Development Helper to view an UpdatePanel request and response. Response messages can be viewed in Partial Rendering mode (shown in Figure 9-22), in raw text mode, or in a Hex mode. Partial Rendering mode is especially useful because it parses the raw response message and makes it easy to view controls that trigger asynchronous postbacks, child UpdatePanel IDs, hiddenFields (ViewState and so on), as well as UpdatePanel response data.

Image from book
Figure 9-22

Web Development Helper allows you to apply filters to limit the types of HTTP requests it monitors. You can access the filtering options by going to Tools Image from book Options Image from book HTTP Logging on the Web Development Helper toolbar. If you’d only like to monitor requests made to .aspx and .asmx files, you can type .aspx,.asmx into the Logging Filter textbox to filter out all other types of requests.


Summary

Understanding the options for debugging ASP.NET AJAX applications is an important part of enhancing an application’s overall performance and stability as well as maximizing developer productivity. In this chapter, you’ve seen how debug and release client-script versions can be loaded using the ScriptMode property of the ScriptManager control. You’ve also seen how to use ASP.NET AJAX Library classes such as Error and Sys.Debug. The Error class allows you to create specific types of Error objects, while the Sys.Debug class allows for tracing, JSON object viewing, assertions, and debugging.

Several options exist for debugging ASP.NET AJAX applications running in Internet Explorer and Firefox. Those running Visual Studio .NET 2005 (or Visual Web Developer Express) will find the debug-ger to be very functional once all scripts referenced in a page are opened using the Script Explorer. Those without access to Visual Studio .NET 2005 can leverage Microsoft’s Script Debugger or use the Firebug debugger with Firefox.

Testing ASP.NET AJAX applications may involve inspecting request and response messages to see if their size can be minimized by reducing ViewState or other data. Several tools exist for viewing request and response messages, such as Fiddler and the Web Development Helper. By using these tools, you can quickly see what data is being sent across the wire.


Chapter 10: Deploying ASP.NET AJAX Applications

When you think of deploying a web application, you may just think of the physical steps necessary to move the application from a development or test machine to the production machine. But of course, there is more to it. In this chapter, you will see that there are really three phases to deployment: preparing for deployment, the deployment itself, followed by monitoring and tuning an application.

Once the application is developed and tested, you need to prepare it for deployment. You can think of this as a pre-deployment checklist. There are a common set of pitfalls that can be avoided by checking and double-checking some settings before proceeding to deployment that will save you time in the long run. You also need to ensure that the production servers have the necessary bits to handle the deployment without error.

The development environment and the production environment are often different. They have different hardware, separate database backends, and unique connection strings. The Web Deployment projects provide an environment for automating the process of deploying your application from a test or development environment into production.

And once the application is in production, you need to know how it is behaving and potentially find ways to optimize for better performance. This chapter offers some options for tuning ASP.NET AJAX for maximum throughput. By leveraging client and server caches, an application can appear faster to the user and significantly reduce the load on the server.

Installing the Bits

There are several pieces to the ASP.NET AJAX functionality. The core ASP.NET AJAX release will ship as part of the .NET Framework in the next release. The CTP will continue as a sample of current investigations and prototypes the team is engaged in. And the ASP.NET AJAX Toolkit is a set of shared source behaviors and control extenders that are central to writing richer web applications. These elements build on top of the platform pieces to provide the key pieces necessary to push web development further.

ASP.NET AJAX

The ASP.NET AJAX Extensions are the core platform for building ASP.NET AJAX applications and the bulk of what this book is about. The object-oriented type system built in JavaScript is transmitted to the client and cached. All subsequent requests for the JavaScript library are satisfied by the browser cache. To configure the web servers to support ASP.NET AJAX applications, the .msi installer file must be run on each of them.

In early preview builds, it wasn’t necessary to install the assembly into the GAC (Global Assembly Cache), but as features were added and refined, it became necessary to be able to assert full trust in order to maintain functionality. Assemblies in the GAC are considered trusted because they must be installed by an administrator. This allows the AJAX Extensions to get access to configuration data and use reflection, both of which are necessary for full functionality. We hope that most hosting providers will install ASP.NET AJAX for use by their clients, so if you use a web server managed by a third party, you must ensure that this has been installed on their servers. To download the latest release of ASP.NET AJAX, visit http://ajax.asp.net/downloads/default.aspx?tabid=47.

You can install ASP.NET AJAX on a server with the default settings without needing to interact with a UI with this command line:

msiexec /i ASPAJAXExtSetup.msi /quiet

One key thing to note with this release is that Microsoft has made the source code available for download. You can actually set a breakpoint and step from your code right into the ASP.NET AJAX platform code and out again. This certainly beats using tools to look at the il (intermediate language) code and try to interpolate when something is not behaving as you would expect. Now you can trace through the code execution from beginning to end. See http://go.microsoft.com/fwlink/?LinkID=82291 to download the source and symbols. If you spot a bug, please report it on the forums: http://forums .asp.net/default.aspx?GroupID=34.

When you deploy an ASP.NET AJAX application, there is no automatic check that ASP.NET AJAX is installed. If you are deploying to a hosted environment on the web, check to see that they have installed ASP.NET AJAX.

The ASP.NET AJAX Control Toolkit

The ASP.NET AJAX Control Toolkit builds on the ASP.NET AJAX Extensions. The Toolkit is a shared source project, meaning that you can download the source code, compile it on your own, and make changes to the source. You can even contribute bug fixes or new pieces of functionality. You can find the Toolkit here:

  • www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=AtlasControlToolkit

The Toolkit contains a set of controls and control extenders with an impressive set of rich behaviors. For example, the ModalPopup control dims the page except for the new layer with the pop-up content. This effect is shown in Figure 10-1.

Image from book
Figure 10-1

This is just one example out of a long list of controls and control extenders. The effects range from drag-and-drop support, to animations, to an autocomplete textbox, as well as rounded corners and drop shadow effects.

The Toolkit does not require installation into the GAC. Instead, you deploy it by simply copying the AjaxControlToolkit.dll into the bin directory of your web application. The controls work in partial trust environments and can be used in hosted environments where permissions are limited.

ASP.NET Futures CTP

The Futures Community Technical Preview (CTP) is used to distribute new ASP.NET AJAX functionality that isn’t quite ready for final release. The CTP is not supported by Microsoft’s Product Support Services, but the ASP.NET team answers questions about it in the forums. The team starts work on a feature and solicits feedback by having volunteers download the CTP to give it a try. In this manner, many developers will have a chance to shape the future. There is no guarantee, however, that the features being shown in the CTP will eventually become part of a supported release, so you should use caution in writing your applications so you don’t become dependent on something that may be obsolete someday. The Futures CTP also installs a new site template into Visual Web Developer, adds items to the web.config file, and puts the Microsoft.Web.Preview.dll in the bin directory.

The CTP continues to be re-released with additional feature investigations. Some of these features make it into the core supported releases with changes that are driven by those who download it, try it, and provide feedback in the forums at http://go.microsoft.com/fwlink/?LinkID=77294.

One key distinction between the ASP.NET AJAX release and the CTP is that the CTP is not installed in the GAC. Instead, it goes in the /bin directory of your application. Any feature that needs full trust must get it by virtue of your application running in full trust. So far, the number of features limited by the need for full trust has been very small, but it is something to check in the release notes if you are deploying to a hosted environment where you run under partial trust.


Preparing for Web Farm Deployment

In many situations, there is no need to deploy an application on multiple servers. The anticipated load is not enough to warrant the extra investment. However, if the load increases and performance tuning can’t gain enough performance, you may find that it’s necessary to scale your web application beyond a single server. A web farm is a cluster of servers all working together to service requests for a single application. The ASP.NET platform scales well from a single server into a web farm environment, but you do need to take a few extra steps to get things set up.

Setting the MachineKey

The primary concerns related to deploying an application to a cluster of servers are not really AJAX-spe-cific. Problems arise when a server handles a request from a browser where the previous request was serviced by a different server. The ViewState data in the page is hashed by a hashing routine tied to the MachineKey, and the hash prevents anyone from modifying the data. To synchronize all the web servers in a web farm, you need to ensure that the MachineKey is the same across all of the applications on all servers. If the MachineKey values are not identical, ViewState sent by one server will be treated as invalid when posted to another server. You can simply take the MachineKey from one machine and copy it across all servers in the web farm, or you can generate your own set of random characters to use at the application scope.

Handling Session State

Session state storage is another concern in web farm environments. The default for session state is to store the data in-memory, so requests that get handled by another server will not have access to the correct session data. To ensure the fastest performance, ASP.NET prefers to use in-memory session state to avoid the need to go to another process or to a database.

To support web farms, however, ASP.NET also provides an out-of-process session state server that can be referenced by multiple servers. This is a Windows service that must run on one of the web servers, and then all the web servers must point to that one as the repository of all session state data. The configuration data indicates that the mode is StateServer instead of InProc. The server running the state service and the port and protocol are also specified.

<configuration>
    <system.web>
        <sessionState mode="StateServer"
                  stateConnectionString="tcpip=dataserver:42424"
                  cookieless="false"
                  timeout="20"/>
        </sessionState>
    </system.web>
</configuration>

The State Server allows you to start sharing sessions across multiple machines but doesn’t scale as well as a backend database. Although leveraging a database as a State Server is slower than the Windows Service, it can eliminate the single-point of failure if you have a robust clustered database. In ASP.NET 1.0, a SQL Server could be configured as the storage point for session information. In ASP.NET 2.0, the provider architecture allows any backend to be plugged in for storing and retrieving session data.

In addition to making the State Server pluggable, Microsoft has released the sources for its SQL session state provider. This is a great resource for developing your own session state provider if you have an environment where one of the shipping providers is not a good match.

<configuration>
  <system.web>
    <sessionState mode="Custom" customProvider="SampleProvider">
      <providers>
        <add name="SampleProvider" type="SampleProvider" />
      </providers>
    </sessionState>
  </system.web>
</configuration>


Using a Different Platform

The Microsoft AJAX Library is available as a standalone .zip file. It includes the script files that are sent down to the browser to enable AJAX functionality. You can deploy these files to any web server on any platform. As already discussed, script files are not versioned like a binary is, so the .zip file includes a path with version information. If you follow this convention when deploying the files, you can easily migrate to a new version of the AJAX Library when it becomes available. The versions are backward-compatible, so you can also run one version of scripts alongside a newer version if some new behavior or change requires updating code. This versioning is helpful in making it easy to switch between versions after validating functionality and features.

When it comes to deployment, if you aren’t using any of the server features of ASP.NET AJAX, you could just put the Microsoft AJAX Library JavaScript files on the server and have them downloaded to the browser.

The three library files all come in release and debug versions.

MicrosoftAjax.debug.js
MicrosoftAjax.js
MicrosoftAjaxTimer.debug.js
MicrosoftAjaxTimer.js
MicrosoftAjaxWebForms.debug.js
MicrosoftAjaxWebForms.js

The release also includes a large set of files in a Globalization subdirectory for greater reach as well as a readme file and a file called pid.txt, which contains the product identifier that you need to provide if you call in a support question to Microsoft.

The ASP.NET team has validated functionality on Internet Explorer and Firefox as well as Safari. There has been quite a bit of testing on Opera, too, where the support of the Microsoft AJAX Library continues to improve.


Avoiding Common Configuration Problems

Now, that you’ve ensured the requisite packages are installed on your production machine, you should guard some common pitfalls before proceeding to deployment. This is a key part of the pre-deployment checklist. Many of the problems in deployment turn out to be errors in the configuration files. Much of the behavior of ASP.NET can be adjusted in the machine.config and web.config files. One change to the root configuration can have a negative impact on all of the applications running on the server. If you change a setting in these files to gather information during development and forget to return it to the default before rolling out to production, you may face performance or security problems.

Disable Debugging

When developing a web application, you often want to be able to step through code as it is running in order to find problems. Visual Studio will even offer to automatically modify the web.config file to enable debugging. This is shown in Figure 10-2. This is the source of a common production problem. The dialog warns that debugging should be disabled in production, but it’s easy to forget.

Image from book
Figure 10-2

The comment that Visual Studio puts in the web.config file is also a reminder to disable it for deployment:

<system.web>
     <!--
        Set compilation debug="true" to insert debugging
        symbols into the compiled page. Because this
    affects performance, set this value to true only
    during development.
-->
<compilation debug="true"/>

In production, the primary symptom of this problem is very slow throughput. Instead of having multiple threads trying to serve many requests simultaneously, ASP.NET will use a single thread to run pages. This, of course, greatly reduces efficiency, and requests will start to queue up waiting for their turn or even get rejected outright. This can go undetected if you aren’t monitoring the site carefully (see “Performance Monitoring,” later in this chapter).

Enable Custom Errors

When an error occurs during the processing of a page, the stack trace information can be very useful in figuring out what is going wrong. But you don’t want this sort of information to be shown to the end user. The behavior of what to show in error conditions is controlled by the customErrors configuration section.

The default setting for custom errors is remoteOnly. This means that when browsing to localhost on a machine, you will get detailed error reporting, but when viewing the site remotely, custom errors are shown instead. The custom error page is normally a user-friendly error page that hides the technical details of what went wrong. This default setting is designed to allow you to get good information during development but to avoid showing potentially sensitive data to the end user. In many development environments, multiple developers are working together on a single application, so they want additional error information when browsing remotely. You can turn off custom errors by setting the mode to off so that you get the same information whether you are browsing locally or not.

<system.web>
       The <customErrors> section enables configuration
       of what to do if/when an unhandled error occurs
       during the execution of a request. Specifically,
       it enables developers to configure html error pages
       to be displayed in place of a error stack trace.

   <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
       <error statusCode="403" redirect="NoAccess.htm" />
       <error statusCode="404" redirect="FileNotFound.htm" />
   </customErrors>

After the error is fixed, it is easy to forget to change the mode back to RemoteOnly or On instead of Off. And you probably won’t notice it until some problem arises in the production environment and you discover that error pages are showing customers stack traces instead of something more friendly. Stack traces can give hints about the source code organization, and those hints can sometimes reveal weaknesses to those who may want to attack your site, so it’s important to hide this from users.

Disable Tracing

Another feature of ASP.NET can result in sharing more information about your server than you intend. The Tracing feature allows you to automatically gather information about the requests being processed. You can then access the trace.axd handler to view the information remotely. This handler is requested from a browser just as though it were a physical page. Remember to view the trace information on the local server if localOnly is true.

<system.web>
   <trace enabled="true" localOnly="true"/>

The tracing output, shown in Figure 10-3, includes information about the most recent requests, their output size, the makeup of the page, and how long it took to process the page lifecycle events. It also includes a listing of various server variables, which is probably more information than you want to share with users!

Image from book
Figure 10-3

The tracing feature is not enabled by default. But if you enable it, remember to disable it again for production web sites. Not only is there the possibility that you are sharing information that you don’t want to, but there is some overhead associated with tracing. If you aren’t using it, there’s no need to tax the performance of your site needlessly.

Set Deployment Mode

Because of the number of configuration settings that are not ideal for production environments, there is a global setting that overrides them. The deployment section has a retail attribute. When set to true, the configuration, tracing, and debug sections are overridden to production settings:

<system.web>
   <deployment retail="true"/>

The deployment setting can only be used at the machine level in the machine.config file. You can’t enable it for just a single application. This setting should always be used on production web servers, so if anyone forgets to modify their web.config on one particular application, this setting will trump those. It’s nice to have a big switch to help you avoid some common mistakes! Be aware, however, that it can also cause confusion when you start toggling one of the other configuration settings and see no effect. Of course, you shouldn’t be doing debugging on a production server anyway, but in rare cases you might need to trace a problem in production that can’t be easily duplicated in a development or test environment.


Creating Web Deployment Projects

Many of the problems that arise when deploying an application stem from differences between the development environment and the production environment. The deployed application may need to use a different database backend. Directory permissions may be different between the test and production systems. To help with these problems, the Visual Web Developer team at Microsoft has released an add-in for Visual Studio to manage building web applications for deployment. This Web Deployment Project add-in is a free tool you can download from Microsoft:

  • http://go.microsoft.com/fwlink/?linkid=55111

The web deployment project uses a new tool called aspnet_merge that can take the individual dlls of a precompiled web application and combine them into a single dll. The default setting is to compile and combine the entire application, including code from the App_Code directory as well as the generated code from .aspx pages, and combine them into a single dll for deployment. This not only simplifies the job of deployment but minimizes the overhead time that would otherwise be required to compile specific pages on the fly. However, there will still be a JIT compilation needed at runtime to convert the IL code to native code. The main disadvantage of using aspnet_merge is that you won’t be able to modify a single page and redeploy it individually, unless you use the partial precompilation method explained later.

After installing the add-in, you create the Web Deployment Project in an existing web site or web application by selecting “Add Web Deployment Project” from the Build menu (see Figure 10-4).

Image from book
Figure 10-4

The default behavior is to reuse the existing project name with a suffix that ends in_deploy, but you can change this naming in the dialog shown in Figure 10-5. It’s important to note that although you can change the location of the deployment project, you can’t select the same root folder as the main project.

Image from book
Figure 10-5

The add-in provides a set of property pages that you can access by right-clicking the deployment project in the Solution Explorer. Before editing the properties, make sure that you’ve selected the right configuration by using the Configuration Manager that can be activated at the top right of the dialog.

Figure 10-6 shows the compilation options. If you check the “Allow this precompiled site to be updat-able” box, it will cause your .aspx files to be deployed verbatim without being precompiled and merged with the other files. This is only a partial precompile, which allows you to modify an individual page later and deploy only that page. If you uncheck that box, however, your .aspx files will be fully precompiled and replaced with files of the same name containing the text “This is a marker file generated by the precompilation tool, and should not be deleted!”

Image from book
Figure 10-6

Figure 10-7 shows the options for the assembly outputs. You can opt to have a single assembly, an assembly per directory, or even produce a separate assembly for each page.

Image from book
Figure 10-7

That gives you greater control over compiled outputs, which is nice, but doesn’t solve the problem of needing different sets of configuration data when deploying. You can manage that on the Deployment property page shown in Figure 10-8, where you can specify configuration sections that should be swapped out for updated versions. Check the “Enable Web.config file section replacement” box and provide the section name and the name of a file that contains the updated section. The file must not have any other sections in it. It needs to be a direct replacement for the original section. Here is an example where the section we want to replace is connectionStrings and the file that will provide the new section is deploymentConnectionStrings.config:

connectionStrings=deploymentConnectionStrings.config
Image from book
Figure 10-8


After Deployment

The application has been developed and tested, the pre-deployment checklist has been satisfied, and deployment is completed. Now it’s time to keep an eye on things and consider what changes might be necessary in the real world. You will need to monitor performance counters and consider other configuration changes to satisfy production demands.

Performance Monitoring

The key point to monitoring the performance of a deployed ASP.NET application is to understand that performance is really a complex balancing act. Incoming requests can be handled very quickly if the content is cached. However, caching content increases the memory consumption and can put additional pressure on the system to page memory to disk. This becomes even more complex when you add that the application is running in a garbage-collected environment that is periodically preempting other work in order to clean up unused memory.

You can gather some data from Windows performance counters on the server in order to monitor how it is running, using the Perfmon tool that comes with Windows. Don’t focus entirely on a single performance metric. You can find an excellent article, “ASP.NET Performance Monitoring, and When to Alert Administrators,” by Thomas Marquardt of the ASP.NET team on the MSDN site at http:// msdn2.microsoft.com/en-us/library/ms972959.aspx. Marquardt recommends a specific set of performance counters as the minimum set to watch. Among other counters, his list includes:

  • Processor(_Total)\% Processor Time and Process(aspnet_wp)\% Processor Time -If the total processor time is consistently high and the ASP.NET worker process isn’t responsible, the web application may be getting starved for processing power because of other things happening on the machine. If the worker process time is consistently high, it may indicate that you are hitting the limit of the load the machine can handle.

  • Process(aspnet_wp)\Private Bytes - The private bytes counter indicates how much memory the process has committed. A steady increase is a good indicator that something in the application is allocating resources that aren’t being returned to the system. This will lead to problems of process recycling and should be investigated.

  • Process(aspnet_wp)\Virtual Bytes - The virtual bytes correspond to the virtual address space the process has consumed. As this approaches the limit of 2GB (or optionally 3GB in 64-bit environments where it is enabled), it indicates that the address space is becoming fragmented. Items held in a cache may still be pinned, while other transient use of memory has been returned. Just as defragmenting your hard drive can improve performance, so can defragment-ing the address space of the worker process. However, there isn’t a great way to do this now, so the solution is to recycle the worker process. ASP.NET will do this for you automatically. The threshold at which this happens is configured as the Maximum Virtual Memory setting, found in the Internet Services Manager on the Recycling tab for the Application Pool.

  • ASP.NET\Application Restarts - Application restarts incur a cost of compilation checks and starting from scratch in populating the cache. Keep an eye out for excessive application restarts that can be caused by tools touching key files like machine.config or web.config. This problem is known to be a side effect of some virus scanning tools.

  • ASP.NET\Requests Rejected - Rejected requests are bad. The server refused to deliver a page to someone trying to access the web application. When requests queued and requests executing exceed a limit, ASP.NET will start rejecting requests in order to catch up.

Monitoring this small set of performance counters can give you insight into how healthy the application is after it has been deployed and can alert you to problems. Several commercial tools are available that will monitor performance counters and can be configured to send mail, page you, or even call you when thresholds are exceeded.

Controlling AJAX Features

The system.web.extensions section itself is essentially all commented out except for the structure definition. By default, the JSON serializer of ASP.NET limits the length of JSON strings that it will consume. If the incoming data exceeds the limit, an exception is thrown. This is to guard against wasting processing power on excessively long strings. The jsonSerialization section lets you customize the limit on JSON strings that will be processed. It can also accept a definition of custom converters for seri-alizing and deserializing additional types.

<system.web.extensions>
  <scripting>
    <webServices>
<!-- Uncomment this line to customize maxJsonLength and add a custom converter -->
<!-
<jsonSerialization maxJsonLength="500">
  <converters>
    <add name="ConvertMe" type="Acme.SubAcme.ConvertMeTypeConverter"/>
  </converters>
</jsonSerialization>
-->

Script access to the ASP.NET application services is not enabled by default; you must turn them on explicitly. The authentication service can be configured to reject requests that aren’t using SSL for security. The profile service must be enabled if you want to use it. You also have to define what properties are available for reading or writing remotely from script.

<!-
 <authenticationService enabled="true" requireSSL = "true|false"/>
-->
<!-
<profileService enabled="true"
                readAccessProperties="propertyname1,propertyname2"
                writeAccessProperties="propertyname1,propertyname2" />
-->
</webServices>

In the scriptResourceHandler section, you control whether or not requests for dynamic script are compressed or cached; normally, you want these features:

    <!-
    <scriptResourceHandler enableCompression="true" enableCaching="true" />
    -->
  </scripting>
</system.web.extensions>

Configuring IIS7

IIS7 also introduces a new configuration section for your web.config file: system.webServer. IIS7 supports two modes: classic and integrated. Classic mode preserves backward compatibility in the way that ISAPI filters and extensions are called. In integrated mode, the old model is replaced by a new pipeline where the two separate lifecycles of IIS and ASP.NET are merged into one for greater efficiency. The ScriptModule is added to the integrated mode pipeline. The httpHandler modifications made in the system.web section are duplicated in the handlers section of the new section.

This change isn’t specific to ASP.NET AJAX but you should be aware of the validation element as it can cause confusion. The validateIntegratedModeConfiguration option is set to false. Setting this value to false means it is not an error to have duplicate entries in the handler and httpHandlers sections and the modules and httpModules sections.

 <system.webServer>
   <validation validateIntegratedModeConfiguration="false"/>
   <modules>
     <add name="ScriptModule" preCondition="integratedMode"
type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    </modules>
    <handlers>
      <remove name="WebServiceHandlerFactory-Integrated" />
      <add name="ScriptHandlerFactory" verb="*" path="*.asmx"
preCondition="integratedMode"
           type="System.Web.Script.Services.ScriptHandlerFactory,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
      <add name="ScriptHandlerFactoryAppServices" verb="*"
  path="*_AppService.axd" preCondition="integratedMode"
           type="System.Web.Script.Services.ScriptHandlerFactory,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
      <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD"
path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" />
    </handlers>
  </system.webServer>

The web.config template is a little large as a starting point but has everything necessary to get started and works across several versions of the web server. One common symptom of a configuration error with the ScriptResource handler is an error message in the browser stating that objects in the Sys namespace are not defined. This wouldn’t be typically associated with deployment, but given the separate definitions for different versions of IIS, it is something to be aware of when moving from test to production environments.


Using Compression for Scripts

Modern browsers have the ability to decompress files, so you can reduce the time needed to download files to the browser by sending them in a compressed format. Text files compress very well. For example, the MicrosoftAjax.js file is about 85KB originally, but when compressed, it is reduced to about 25KB. There is some extra CPU usage on the server to do the compression, but the compressed scripts can be cached on the server, so the processing overhead shouldn’t matter much. The client will decompress the file once and then cache it as well so the impact there is minimal and the overall effect can be a benefit to the server and to the client.

Compression of Dynamic Scripts

Scripts embedded as a resource within a dll and extracted by ASP.NET on demand are referred to as dynamic scripts. ASP.NET AJAX supports compressing those scripts as they are extracted from the dll. Custom controls that use the ScriptManager to access their dynamic scripts will also get the benefit of having their JavaScript compressed and cached.

The compression of dynamic scripts is controlled in the web.config file. The enableCompression and enableCaching attributes should be set to true:

<system.web.extensions>
  <scripting>
    <scriptResourceHandler enableCompression="true" enableCaching="true" />
  </scripting>
</system.web.extensions>

Note that there are some tradeoffs in using compression and caching. Using compression without caching will make the server perform the compression work for every request. This can increase the load significantly on the server. However, caching the scripts can increase the memory used by the ASP.NET worker process, putting pressure on the cache and affecting the overall throughput as a result.

On the other hand, the compressed scripts are smaller and thus transmitted more quickly to the browser, freeing up a connection and thread more quickly. Most environments will benefit from allowing the Script Resource Handler to compress and cache scripts, but busy servers need to be monitored to watch out for possible negative impacts to memory or processor usage.

Compression of Static Scripts

IIS can compress static scripts to get the same benefit. In IIS7, JavaScript files are compressed by default. In IIS6, this is not the default. Instead, it has to be enabled, and there are several steps necessary to set it up.

First, use the Internet Services Manager to go to the property pages for the web site. On the services tab, check the Compress static files check box, and specify a directory for the server to use in caching the compressed output. This saves the overhead of recompressing the content after a process restart.

The MMC snap-in does not expose one key setting that should be changed to enable compression of the .js files for both standard compression types that are supported. You have to make the change in the IIS6 metabase directly. You can use the adsutil.vbs file installed in \WINNT\System32\Inetpub\AdminScripts.

To enable IIS to use deflate and gzip compression on .js files, use the following command lines:

cscript.exe adsutil.vbs set w3svc/Filters/Compression/DEFLATE/HcFileExtensions "js"
cscript.exe adsutil.vbs set w3svc/Filters/Compression/GZIP/HcFileExtensions "js"

Now, IIS will compress static JavaScript files and cache them to disk. However, there is still one other change needed for IIS6. To avoid getting compressed scripts cached by proxy servers and gateways and returned to browsers that don’t support compression, the cache expiration header is set to a date in the past. When IIS6 was released, the level of support for compression in browsers was still lacking, but now it’s safer to assume that the browsers accessing a site have such support. If you leave a backdated expiration date, the browser will request the AJAX scripts on every page access to an AJAX-enabled page. I’d rather send 100K of uncompressed script to the browser once and have it cached than send 25K of compressed script on every page request. You can view the parameters being used for compression using the adsutil script and then modify them to a date you are comfortable with by using a set parameter instead of enum:

cscript.exe adsutil.vbs enum w3svc/filters/compression/parameters

You should look at the output and consider your application variables before setting a new expiration to ensure that you are picking an appropriate expiration for your environment.

Consider Using Shared Scripts

High-volume applications make use of edge servers to maximize the use of their hardware. They move lots of static content to a separate server in order to partition the load. The web server can deliver a lot of static content very fast and with low CPU impact. Multiple applications that all make use of the same set of resources gain an advantage when you coalesce them to a single place referenced by all applications. This also applies to a lot of ASP.NET AJAX applications on the same server, all serving up the same set of JavaScript files.

The ScriptManager control has a ScriptPath property that can be set to an absolute path to make use of a copy of the scripts shared by multiple applications and directories. This can avoid the impact of many different applications consuming additional memory to cache the scripts and bypasses the need to compress them again.

The ScriptManager uses the ScriptPath as the root location to search for scripts and takes into account the assembly name and version. If want to cache the scripts in a folder under the root of the web site, you thus need to copy them from their default location under C:\Program Files\Microsoft ASP.NET. For example, you could set the ScriptPath="/scripts" and then copy the files into C:\InetPub\wwwroot\scripts\System.Web.Extensions\1.0.61025.0\, assuming your web root folder is C:\InetPub\wwwroot.

Don’t Change Version Paths

The ScriptPath setting of the ScriptManager applies to all of the ScriptReferences that it contains. However, the ScriptReference has a Path property that can override that setting. You may be tempted to override the path directly to put the AJAX scripts in a simpler path without the version, but I recommend against it. The use of the version number identifies the scripts so that servicing and subsequent releases will use a different folder to avoid problems.

If you switch from the dynamic scripts to the static versions, you also need to be aware of a servicing concern. If the ASP.NET team releases an updated version of the scripts, you will need to update your deployed copy in order to get the fix. It will no longer be sufficient to just install the updated System.Web.Extensions.dll in the GAC.

Tip

If you install a new version of System.Web.Extensions.dll in the GAC and create a new version-coded folder to hold the scripts, you may also want to keep the old dll in the GAC and keep the old scripts in place to service older applications that haven’t been updated yet.


Summary

Deploying ASP.NET AJAX applications is not too complex, but there are some pitfalls to be avoided. Configuration errors are the most common complaint. This is made worse by the fact that development environments are often different from the production environment, and configuration changes are a necessary part of deployment. Before beginning deployment, you should check for a set of common issues that cause problems and can be difficult to track down and are also easily avoided.

The Web Deployment Project add-in for Visual Web Developer can help in automating a reusable process for moving from development into production. It allows for extensive customization for your unique environment and makes it easy to establish an environment where designers and developers collaborate to produce the application and then automated processes are employed to move it into production reliably.

The increased use of JavaScript brings some potentially interesting challenges to high volume applications that you want to tune for the absolute best performance. Coalescing script use to a single location for multiple applications can provide a performance benefit.

For single applications that deploy to a cluster of web servers, there are additional configuration concerns. Machine keys need to be synchronized and you may need to use a backend database to store Session state.

Outside of an ASP.NET application, it is still possible to use the Microsoft AJAX Library. This allows you to use the rich object-oriented approach to writing JavaScript in various environments and to avoid another context switch for the developer using multiple libraries.


Chapter 11: Building Custom Controls

Overview

A lot of features are packed into the ASP.NET AJAX framework. As a result, many common tasks can be accomplished using existing controls that handle making asynchronous calls to ASP.NET pages or web services. Many of the complexities associated with making asynchronous calls are hidden so that you can focus on building the application rather than on the message processing that happens behind the scenes. A perfect example of this is the UpdatePanel control, which makes it easy to add AJAX functionality into an ASP.NET page without writing a lot of code. Simply add an UpdatePanel to an ASP.NET page and you have instant AJAX functionality available even if you’ve never made an asynchronous call before in your life.

No matter how many features are built into a framework, there will always be times when you need to build custom controls that wrap complex functionality or refactor code that may have previously been duplicated in multiple places. Writing custom ASP.NET AJAX controls is quite different from writing standard ASP.NET controls because the bulk of a control’s functionality is defined in a JavaScript file as opposed to VB.NET or C# files. However, many of the same concepts apply, such as fields, properties, methods/functions, events, and event handlers.

In this chapter, you’ll learn how to build custom ASP.NET AJAX controls capable of generating dynamic output, handling end user interactions, and connecting to web services. Topics covered include the overall structure of client-side controls as well as how constructors, properties, functions, events, and event handlers can be defined and used. You’ll also see how client-side scripts can be encapsulated in server controls and exposed using familiar ASP.NET server control syntax. Once this is done, you can simply drop a server control on a form and set some properties to inject client script into the response stream at runtime. Let’s get started by analyzing the structure of custom client-side controls and discussing key classes provided by the ASP.NET AJAX script library.


Building Client-Side ASP.NET AJAX Controls

JavaScript files can be written using several different techniques. A great majority of scripts on the Internet consist of multiple functions thrown into one or more files used to perform client-side functionality. While these types of unstructured scripts certainly get the job done, they don’t encapsulate related functions into containers for better code reuse and organization. Relatively few scripts rely on object-ori-ented techniques where JavaScript classes are defined using the JavaScript function and prototype design pattern.

Although JavaScript is not a true object-oriented language, it is capable of emulating several object-ori-ented characteristics that can be used to build new types, extend existing types, and in general create a reuseable client-side object library. Building custom AJAX controls requires understanding how to leverage encapsulation features built into the JavaScript language that can be used to create classes. It also requires an understanding of how the ASP.NET AJAX script library extends JavaScript’s default set of features.

Terminology between JavaScript and VB.NET or C# objects is surprisingly similar. Functionality can be grouped into classes, although JavaScript doesn’t technically support the concept of a class and doesn’t use the class keyword. However, JavaScript does support organizing related functionality into container objects much like VB.NET and C# do. Both JavaScript and .NET “classes” can expose fields, properties, and methods, although methods are typically referred to as “functions” in JavaScript, as they are defined using the function keyword. To keep things consistent, the remainder of this chapter will use standard .NET terminology and refer to actions a JavaScript object performs as methods rather than functions. It will also refer to variables as fields and characteristics/attributes of an object as properties.

Extending JavaScript

The ASP.NET AJAX script library extends JavaScript in several ways by allowing namespaces, classes, enumerations, and even interfaces to be defined and used on the client side. These extensions provide a familiar framework that resembles the server-side environment most ASP.NET developers are accustomed to working with and using. As a result, ASP.NET and JavaScript developers alike can create custom ASP.NET AJAX controls rather quickly once a few core concepts are understood.

One of the key classes involved in extending JavaScript’s existing functionality is the ASP.NET AJAX Type class. The Type class exposes several different methods that are useful when building custom controls as shown in the following table.

Open table as spreadsheet
Method | Description
callBaseMethod | Returns a reference to a base class’s method for the specified instance. Typically used in a control’s initialize() method and dispose() method when building custom client-side controls.
implementsInterface | Determines if a type implements a specific interface. Returns a Boolean value.
InheritsFrom | Determines if a type inherits from a specific base type. Returns a Boolean value.
initializeBase | Initializes the base type of a derived type. A call to the initializeBase() method is made in a control’s constructor.
registerClass | Registers a specific type with the ASP.NET AJAX client-side framework. A base class and one or more interfaces can optionally be defined when calling registerClass().
registerInterface | Registers an interface with the ASP.NET AJAX client-side framework.
registerNamespace | Registers a namespace with the ASP.NET AJAX client-side framework.

When building a custom AJAX control, you use the Type class within client-side code to define a names-pace, register a class, and possibly even an interface or enumeration with the ASP.NET AJAX framework. You’ll also use the Type class when a custom control needs to inherit functionality from an existing class in the ASP.NET AJAX script library. Every client-side control used in an ASP.NET AJAX application is considered to be a “type,” just as any VB.NET or C# class built using the .NET framework is considered to be a “type.” As a result, methods exposed by the ASP.NET AJAX Type class are available on all AJAX controls that you create.

In addition to the Type class, the ASP.NET AJAX script library provides several different classes that can serve as the foundation for a custom control depending upon the control’s intended purpose. Three main categories of custom controls can be built, including behaviors, visual controls, and nonvisual controls. In cases where you’d like to extend an existing DOM element’s behavior (to add additional style information to a button or textbox, for instance), the Sys.UI.Behavior class can be inherited from to create a custom behavior control. While controls that derive from Sys.UI.Behavior extend the overall behavior of a DOM element, they don’t modify the original purpose or intent of the control. When visual controls need to be created that modify the original intent of a DOM element and extend the element’s functionality, the Sys.UI.Control class can be used as the base class for the control. Nonvisual controls that don’t extend the behavior of a DOM element or extend its overall functionality (a timer for example) should derive from the Sys.Component base class.

The custom control used throughout this chapter derives from the Sys.UI.Control class, as it extends the functionality of a DOM element and adds significant functionality to it. Inheriting from Sys.UI.Control provides a solid starting foundation that can be built upon rather than writing the entire control from scratch. The control that we’ll develop here is named AlbumViewer and allows users to view one or more albums, and to search through them using asynchronous calls made to a .NET web service. Figure 11-1 shows the output generated by the AlbumViewer control.

Image from book
Figure 11-1

To create the AlbumViewer control, a prescribed set of steps can be followed. These steps are fairly standard and should be followed when building ASP.NET AJAX client-side controls in general.

  1. Register a namespace - Register the namespace for the control using the Type class’s registerNamespace() method.

  2. Create a constructor - Create the control’s constructor and pass in the DOM element that represents the container for the control as a parameter. Initialize the base class by calling the initializeBase() method and define any fields that will be used by the control to store data.

  3. Prototype properties and methods - Follow the prototype design pattern to define properties and methods exposed by the control. Properties and methods are defined using JSON notation.

  4. Override initialize() - Within the prototype section of the control, initialize the control by overriding the initialize() method. Within initialize you can dynamically assign values to fields defined in the constructor, create delegates, handle events, plus more.

  5. Override dispose() - Within the prototype section of the control, override the dispose() method to allow objects such as delegates used by the control to be cleaned up properly.

  6. Register the control and inherit from a base class - Register the control with the ASP.NET AJAX client-side object model by using the Type class’s registerClass() method. Pass the appropriate base class to derive from as a parameter to registerClass().

Take a look at how a custom AlbumViewer control can be created and used in an ASP.NET AJAX application.

Tip

All of the code that follows for the AlbumViewer client-side control can be found in the Scripts/ AlbumViewerStandAlone.js file or in the AlbumViewer class library project available in the code download for this book on the WROX website. The individual code listings that follow represent only a portion of the overall control’s code and will not run on their own.

Registering a Control Namespace

When creating custom client-side controls, you’ll want to define a namespace for each control to resolve any possible naming conflicts that may arise in an application. The AlbumViewer control is placed in a Wrox.ASPAJAX.Samples namespace and registered by using the Type class’s registerNamespace() function. Namespaces can contain other nested namespaces, classes, interfaces, enumerations, and even delegates and should be defined at the beginning of a control’s code:

Type.registerNamespace("Wrox.ASPAJAX.Samples");

Note that no End Namespace keywords (as in VB.NET) or start and end brackets (as in C#) are used to define where an ASP.NET AJAX client-side namespace starts and ends. Instead, the namespace is referenced directly by a given control, as shown next.

Creating a Control Constructor

Once the namespace for a control is registered using the Type class, the control itself can be defined using the JavaScript "function" keyword. This is done by prefixing the control’s name with the names-pace that it should be placed in and defining an anonymous JavaScript function as shown in Listing 11-1.

Listing 11-1
Image from book

Type.registerNamespace("Wrox.ASPJAX.Samples");

Wrox.ASPAJAX.Samples.AlbumViewer = function(element) {
    // Initialize base class
    Wrox.ASPAJAX.Samples.AlbumViewer.initializeBase(this,[element]);
    this._AlbumDivOverCssClass = null;
    this._AlbumDivOutCssClass = null;
    this._AlbumTitleCssClass = null;
    this._AlbumArtistCssClass = null;
    this._SongTitleCssClass = null;
    this._Albums = [];
    this._ServicePath = null;
    this._ServiceMethod = null;
    this._StatusLabel = null;
    this._ShowSearchBox = false;
    this._albumContainer = null;

    //Delegate definitions
    this._overHandler = null;
    this._outHandler = null;
    this._clickHandler = null;
    this._btnClickHandler = null;
    this._propertyChanged = null;
}
Image from book

The code in Listing 11-1 associates the namespace of Wrox.ASPAJAX.Samples with the AlbumViewer control and creates a constructor for the control by defining a new anonymous JavaScript function. Like VB.NET and C# constructors, JavaScript constructors can accept initialization parameters. The AlbumViewer constructor shown in Listing 11-1 accepts a DOM element object representing the parent container that the control will use. The AlbumViewer control uses a div DOM element as its parent container.

The first line of code inside of the AlbumViewer control’s constructor makes a call to the Type class’s initializeBase() method (recall that all controls are considered types and have access to the Type class’s methods) to initialize the control’s base class. Because the AlbumViewer control is a visual control that adds content to a div element, it inherits from the Sys.UI.Control base class provided by the ASP.NET AJAX script library. The call to initializeBase() instantiates the Sys.UI.Control class. You’ll learn more about how the AlbumViewer class inherits from Sys.UI.Control later in the chapter.

The initializeBase() method accepts two parameters. The first parameter represents the object instance to initialize the base class for (the AlbumViewer control, in this case). You’ll normally use the JavaScript this keyword for the parameter value. The second parameter accepts an array, which is why the [ and ] characters are used, that can be passed to the base constructor as it initializes. Listing 11-1 passes the container element of the AlbumViewer control as the parameter value. Because the control uses a div element as its container, the div is passed directly to the base class’s constructor.

Once the base class has been initialized, fields used by the AlbumViewer control are defined. Each field is prefixed with the this keyword, which represents the current class (the this keyword is much like the Me keyword VB.NET developers use and analogous to the this keyword in C#). Because the fields shown in Listing 11-1 are private, their names are prefixed by the underscore character. This character isn’t required and has no actual significance; it is a standard naming convention used in the ASP.NET AJAX script library for private members of a class. As a general rule, all object and array fields used by a control should be defined in the control’s constructor so that the field instances are unique to all control instances and not shared across instances.

The AlbumViewer class defines several fields in its constructor such as this._AlbumTitleCssClass and this._AlbumArtistCssClass that are used to store the CSS class applied to various child elements within the control. It also defines an array named this._Albums using the shortcut []JSON notation. this._Albums is used to store album information used by the control. Other fields such as this._ServicePath and this._ServiceMethod are used to store information about the web service that provides album, artist, and song information to the control. The handler fields shown (this._overHandler, this._outHandler, and so on) represent delegates used by the control that route data from events to event handlers.

Using the Prototype Design Pattern with JSON

JavaScript has long supported the ability to extend objects by using the prototype property. By using it, JavaScript objects can inherit from base types and add additional properties and methods. The ASP.NET AJAX Library uses the prototype property and a pattern referred to as the “prototype design pattern” to extend existing objects and create new ones. Listing 11-2 shows how the prototype property can be used to define a Person object and expose two property getters and setters, as well as a method.

Listing 11-2
Image from book

function Person() {
    this._FirstName = null;
    this._LastName = null;
}

Person.prototype.get_FirstName = function() {
      return this._FirstName;
}

Person.prototype.set_FirstName = function(value) {
      this._FirstName = value;
}

Person.prototype.get_LastName = function() {
      return this._LastName;
}

Person.prototype.set_LastName = function(value) {
      this._LastName = value;
}
Person.prototype.getFullName = function() {
      return this._FirstName + " " + this._LastName;
}
Image from book
Tip

JavaScript doesn’t officially support the concept of property getters and setters as in VB.NET or C#. However, getters and setters can be emulated by writing the corresponding methods. For example, get_FirstName represents the get block while set_FirstName represents the set block. Both of these are officially methods in .NET terms and functions in JavaScript terms but are typically treated as properties, as they get and set the value of a field.

Usually, there is a one-to-one relationship between a prototype property and a function definition, as shown in Listing 11-2. If four methods need to be added to a custom control, the prototype property is used four times. An exception to this rule would be for objects that define their properties and methods directly in the constructor, which is not a practice followed by the ASP.NET AJAX script library. Properties and methods added directly into the constructor get duplicated each time a new object instance is created. However, by using the prototype property, properties and methods are shared across multiple object instances resulting in less bloat.

Although the code shown in Listing 11-2 is fairly standard, ASP.NET AJAX client-side controls use the prototype design pattern with JSON notation to define properties and methods rather than using the prototype property for each property or method. Listing 11-3 shows how the Person class shown in Listing 11-2 can be refactored using the prototype design pattern with JSON. As you look through the code, notice that all properties and methods are wrapped within a single prototype statement. Property and method names are following by a colon that separates the name from the actual functionality. Each property and method is then separated by a comma.

Listing 11-3
Image from book

function Person() {
    this._FirstName = null;
    this._LastName = null;
}

Person.prototype = {
    get_FirstName: function() {
       return this._FirstName;
    },

    set_FirstName: function(value) {
       this._FirstName = value;
    },

    get_LastName: function() {
       return this._LastName;
    },

    set_LastName: function(value) {
       this._LastName = value;
    },

    getFullName: function() {
       return this._FirstName + " " + this._LastName;
    }
}
Image from book

The functionality for a property or method can also be defined in a separate location rather than inline, as shown in Listing 11-4. The ASP.NET AJAX script library uses this technique quite frequently to define client-side classes.

Listing 11-4
Image from book

function Person() {
    this._FirstName = null;
    this._LastName = null;
}

Person.prototype = {
    get_FirstName: Person$get_FirstName,

    set_FirstName: Person$set_FirstName,

    get_LastName: Person$get_LastName,

    set_LastName: Person$set_LastName,

    getFullName: Person$getFullName
}

function Person$get_FirstName() {
    return this._FirstName;
}

function Person$set_FirstName(value) {
    this._FirstName = value;
}

function Person$get_LastName() {
    return this._LastName;
}

function Person$set_LastName(value) {
    this._LastName = value;
}

function Person$getFullName() {
    return this._FirstName + " " + this._LastName;
}
Image from book

Now that you’ve seen the fundamentals of building constructors and using the prototype design pattern, let’s take a look at some of the properties defined in the AlbumViewer control.

Defining Control Properties

Properties in client-side controls work in the same way as properties in server-side controls. However, the way properties are defined in client-side controls is quite different because JavaScript doesn’t support the .NET property syntax that formally defines get and set blocks. As an example, to define a getter and setter for a property named Albums that reads and writes from a private field named this._Albums, the following code could be written using the prototype design pattern and JSON:

get_Albums: function() {
    return this._Albums;
},

set_Albums: function(value) {
    this._Albums = value;
}

The get block normally found in VB.NET or C# classes is simulated by adding get to the front of the property name while the set block uses set_. Any keywords can be used, but get_ and set_ are used throughout the ASP.NET AJAX script library and are also used in the AlbumViewer control for the sake of consistency.

The AlbumViewer control defines several different properties used to control CSS styles, searching capabilities, and web service calls. Each property acts as a gateway to private fields defined in the control’s constructor shown in Listing 11-1. A list of properties exposed by the AlbumViewer control is shown in the following table. Keep in mind that although these properties are really JavaScript functions prefixed by get_ and set_ keywords, their only purpose is to get and set data, so we’ll refer to them as properties to stay consistent with VB.NET and C# property definitions. However, the class consumer needs to use a function syntax when calling the getter or setter, as there is no explicit property syntax support in JavaScript.

Open table as spreadsheet
Property Name | Description
Albums | Gets and sets an array of Albums objects.
AlbumDivOverCssClass | Gets and sets the CSS class name to use when a user hovers over an album in the control.
AlbumDivOutCssClass | Gets and sets the CSS class name to use when a user moves the mouse out of an album in the control.
AlbumTitleCssClass | Gets and sets the CSS class name to use for an individual album title.
AlbumArtistCssClass | Gets and sets the CSS class name to use for an album artist.
SongTitleCssClass | Gets and sets the CSS class name to use for album songs.
ShowSearchBox | Gets and sets a Boolean value that is used by the AlbumViewer control to determine if an album search textbox should be shown or not.
ServicePath | Gets and sets the path to the album web service. The service is used to retrieve album data dynamically.
ServiceMethod | Gets and sets the Web Method to call on the album web service.

Listing 11-5 shows how the properties in the previous table are defined and used within the AlbumViewer control’s prototype definition. Each property has an associated getter and setter used to access the associated field variable defined in the control’s constructor.

Listing 11-5
Image from book

Wrox.ASPAJAX.Samples.AlbumViewer.prototype = {
    get_Albums: function() {
        return this._Albums;
    },

    set_Albums: function(value) {
        this._Albums = value;
    },

    get_AlbumDivOverCssClass: function() {

    return this._AlbumDivOverCssClass;
},

set_AlbumDivOverCssClass: function(value) {
    this._AlbumDivOverCssClass = value;
},

get_AlbumDivOutCssClass: function() {
    return this._AlbumDivOutCssClass;
},

set_AlbumDivOutCssClass: function(value) {
    this._AlbumDivOutCssClass = value;
},

get_AlbumTitleCssClass: function() {
    return this._AlbumTitleCssClass;
},

set_AlbumTitleCssClass: function(value) {
    this._AlbumTitleCssClass = value;
},

get_AlbumArtistCssClass: function() {
    return this._AlbumArtistCssClass;
},

set_AlbumArtistCssClass: function(value) {
    this._AlbumArtistCssClass = value;
},

get_SongTitleCssClass: function() {
    return this._SongTitleCssClass;
},

set_SongTitleCssClass: function(value) {
    this._SongTitleCssClass = value;
},

get_ShowSearchBox: function() {
    return this._ShowSearchBox;
},

set_ShowSearchBox: function(value) {
    this._ShowSearchBox = value;
},

get_ServicePath : function() {
    return this._ServicePath;
},

set_ServicePath : function(value) {
    if (this._ServicePath != value) {
        this._ServicePath = value;
            this.raisePropertyChanged('ServicePath');
        }
    },

    get_ServiceMethod : function() {
        return this._ServiceMethod;
    },

    set_ServiceMethod : function(value) {
        if (this._ServiceMethod != value) {
            this._ServiceMethod = value;
            this.raisePropertyChanged('ServiceMethod');
        }
    }

    //More code follows for methods and event handlers
}
Image from book

The majority of the properties shown in Listing 11-5 simply read and write from private fields. While the ServicePath and ServiceMethod properties follow this same pattern, the setter for each of these properties adds a call to a method named raisePropertyChanged(). Calling this method is useful in situations where a control needs to be notified when a property changes so that it can act upon the change. This is a simple analog of the .NET event mechanism. The AlbumViewer control requires notification if the ServicePath or ServiceMethod properties are changed by the consumer of the control so that it can retrieve fresh album data.

You may wonder where the raisePropertyChanged() method is defined. Fortunately, it’s not something you have to code manually. The AlbumViewer control derives from the Sys.UI.Control class, which in turn derives from the Sys.Component class. The Sys.Component class defines the raisePropertyChanged() method as one of its members (for a complete listing of the Control class’s properties and methods refer to the ASP.NET AJAX documentation). When raisePropertyChanged() is called, an event is raised that can be handled by an event handler. The AlbumViewer control handles the property changed event as the ServicePath and ServiceMethod properties change and uses the two property values to call the appropriate web service to retrieve new album data and load it into the control. Additional information about handling events such as the one raised by raisePropertyChanged() is covered in the next section.

Initializing a Control and Handling Events

The Sys.Component class mentioned earlier defines an initialize() method that’s useful when a control is first initialized. When initialize() is called, a property named isInitialized is set to true by Sys.Component through a call to set_isInitialized(). This property is referenced by other classes in the ASP.NET AJAX script library such as Sys.UI.Behavior to determine if a control has been initialized or not.

Custom controls such as the AlbumViewer control can override the initialize() method and use it to initialize a base class, initialize properties or field values, define delegates, and hook events to event han-dlers. The AlbumViewer control defines the initialize() method within the prototype section of the code shown in Listing 11-6.

Listing 11-6
Image from book

Wrox.ASPAJAX.Samples.AlbumViewer.prototype = {

    //Property definitions go here

    initialize : function() {
        var e = this.get_element();
        e.style.className = this.get_AlbumDivOutCssClass();

        //Initialize Delegates
        this._overHandler = Function.createDelegate(this, this._onMouseOver);
        this._outHandler = Function.createDelegate(this, this._onMouseOut);
        this._clickHandler = Function.createDelegate(this, this._onClick);
        this._btnClickHandler = Function.createDelegate(this,this._onButtonClick);
        this._propertyChanged =
          Function.createDelegate(this,this._onPropertyChanged);
        this.add_propertyChanged(this._propertyChanged);

        //Create search text box
        if (this._ShowSearchBox) {
            var lblText = document.createElement("SPAN");
            lblText.style.cssText = "font-family:arial;font-weight:bold;" +
              "font-size:8pt;";
            lblText.innerHTML = "Album Name: ";

            var txtAlbum = document.createElement("INPUT");
            txtAlbum.setAttribute("type","text");
            txtAlbum.setAttribute("id",e.id + "_txtAlbumSearch");
            txtAlbum.style.cssText = "width:150px;";

            var btnAlbumSearch = document.createElement("INPUT");
            btnAlbumSearch.setAttribute("type","button");
            btnAlbumSearch.id = e.id + "_btnAlbumSearch";
            btnAlbumSearch.value = "Search";
            $addHandler(btnAlbumSearch,"click",this._btnClickHandler);

            e.appendChild(lblText);
            e.appendChild(txtAlbum);
            e.appendChild(btnAlbumSearch);
            e.appendChild(document.createElement("P"));
        }

        //Create div that will hold albums
        this._albumContainer = document.createElement("DIV");
        this._albumContainer.id = e.id + "_AlbumContainer";
        e.appendChild(this._albumContainer);

        Wrox.ASPAJAX.Samples.AlbumViewer.callBaseMethod(this, 'initialize');

        //Bind data if albums have been assigned
        if (this._ServicePath && this._ServiceMethod) {
            // Call the web service
            this._invokeWebService(null);
        } else if (this._Albums != null) {
            this.set_data(this._Albums);
        }
    },

    //Additional methods go here
}
Image from book

When the AlbumViewer control’s initialize() method is called, several tasks are performed. First, a reference to the DOM element used by the control is made by calling get_element(). The element property is inherited from Sys.UI.Control and is referenced any time the AlbumViewer control needs to access the DOM element container. Once the DOM element is accessed, the CSS class to apply to the element is assigned by reading from the AlbumDivOutCssClass property. This class defines the overall style applied to the control’s parent container.

Next, several delegates are created that point to various event handler methods. Delegates in ASP.NET AJAX client-side controls perform the same overall function as delegates used in VB.NET or C#: They pass data raised by an event to an event handler. Client-side delegates are created by calling the Function class’s createDelegate() method and passing the object context that the delegate applies to, as well as the name of the event handler method that the delegate should direct data to. The delegates created in the AlbumViewer control are used to route data when mouseover, mouseout, click, and propertychanged events are raised.

The delegate instances returned from calling Function.createDelegate() are assigned to private fields defined in the control’s constructor because several of them are reused throughout the control and need to be globally accessible. For example, the following code creates a delegate that is responsible for routing mouseover event data to an event handler named _onMouseOver. A reference to the delegate is stored in the this._overHandler field.

this._overHandler = Function.createDelegate(this, this._onMouseOver);

Property changed events raised by the AlbumViewer control are routed to an event handler by calling the add_propertyChanged() method. This method routes data raised when the raisePropertyChanged() method discussed earlier is called to an event handler so that the change can be handled by the control. It accepts the name of the event handler method to call or a delegate instance that points to the method.

Once a delegate is created, it can be used to hook an event directly to an event handler using the ASP.NET AJAX $addHandler method. VB.NET developers will be quite comfortable using the $addHandler() method, as it’s similar in functionality to the VB.NET AddHandler keyword. $addHandler() is a shortcut to the Sys.UI.DomEvent class’s addHandler() method. It accepts several parameters, including the object that raises the event, the name of the event that will be raised, and the delegate that should be used to route event data to an event handler. Listing 11-6 contains an example of using the $addHandler method to hook up a button control’s click event to a delegate named this._btnClickHandler:

//Define Delegate
this._btnClickHandler =
    Function.createDelegate(this,this._onButtonClick);

//Hook button's click event
$addHandler(btnAlbumSearch,"click",this._btnClickHandler);

In addition to $addHandler(), the ASP.NET AJAX script library provides an $addHandlers() method that allows multiple event handlers to be defined for a DOM element with a single statement. For example, if you need to attach the mouseover, mouseout, and click events of a DOM element named albumDiv to event handlers, you could use $addHandlers() in the following way:

$addHandlers(albumDiv,
   {"mouseover" : this._overHandler,
    "mouseout" : this._outHandler,
    "click" : this._clickHandler },
  this);

The second parameter passed to $addHandlers is a JSON object containing the different event names and related delegates that should be attached to the DOM element.

The initialize() method shown in Listing 11-6 is also used to add several controls into the AlbumViewer control’s parent container element. Because album data is being displayed, an end user may want to search for one or more albums based upon title. Searching is controlled by data stored in the this._ShowTextBox field of the control. When the field value is true, a span tag, textbox, and but-ton are added to the AlbumViewer control’s div container. End users can then use the controls to perform a search. Adding controls is accomplished by calling document.createElement() and passing in the name of the element object that should be created. Once element objects are created, they’re added to the appropriate parent container by calling its appendChild() method.

The final two tasks performed by the AlbumViewer control’s initialize() method are calling the base Sys.UI.Control class’s initialize() method and binding album data to the control. The Type class’s callBaseMethod() method calls the initialize() method of the base class. This method accepts the current object instance as well as the name of the method to call on the base class. Although not used by the AlbumViewer control, callBaseMethod() returns a reference to the base class’s ini tialize() method.

Now that you’ve been exposed to the initialize() method, let’s move on to a few other methods used by the AlbumViewer control to bind data, call web services, and perform additional functionality.

Defining Control Methods

The AlbumViewer control provides several ways for an end user to interact with the control to view albums, artists, and songs. Users can search for albums using all or part of the album name, highlight albums as they move their mouse, and click albums to view more data about an album. Each of these interactions requires event handlers to be written to handle the performed action. In addition to supporting user interactions, the AlbumViewer supports calling a web service to retrieve album data as well as data binding. All of this functionality is handled by methods defined within the AlbumViewer control.

Methods used by the control are shown in the following table. Methods that start with an underscore are considered private, although JavaScript doesn’t truly support access modifiers such as public or private.

Open table as spreadsheet
Method Name | Description
initialize | Initializes the control as well as the base class.
dispose | Cleans up resources used by the control such as event handlers.
set_data | Binds album data supplied by the client or by a web service to the control. Performs the bulk of the control’s functionality. This method is similar in purpose to the DataBind() method found on ASP.NET server controls.
_getAlbumNode | Retrieves a specific album object within the AlbumViewer control.
_invokeWebService | Calls a web service based upon values supplied by the ServicePath and ServiceMethod properties.
_onButtonClick | Handles the search button’s click event.
_onClick | Handles the click event for album divs in the control.
_onMouseOut | Handles the mouseout event for album divs in the control.
_onMouseOver | Handles the mouseover event for album divs in the control.
_onMethodComplete | Processes data returned from a web service call and binds the data by calling set_data().
_onMethodError | Handles errors returned from calls made to a web service.
_onPropertyChanged | Handles property changed events that occur as the ServicePath and ServiceMethod property values change.
_setStyle | Used to apply a CSS style to an album div as the user interacts with the AlbumViewer control.

The AlbumViewer control’s set_data() method performs the bulk of the work done by the control. It accepts a single parameter that contains the Album objects to bind to the control and display to the end user. Although Album objects aren’t the focus of this chapter, it’s important to note that many of the same steps followed when building a custom control are also applied to building custom client-side classes. Listing 11-7 shows a representation of the Album and Song classes consumed and bound by the set_data() method. These classes are used when a client script creates objects and passes them to the control’s set_data() method. When the AlbumViewer control calls a web service, the JSON Album objects returned from the service are used instead.

Tip

The fields defined in the Song and Album class constructors aren’t prefixed with an underscore character (to simulate a private field) so that the objects are more in line with JSON objects returned from web service calls that have properties defined such as Title, Artist, ImageUrl, and Songs for the Album class. By matching up the Album class shown in Listing 11-7 with the objects exposed by the web service, the AlbumViewer control can work in a flexible manner with both types of objects.
Listing 11-7
Image from book

Type.registerNamespace("Wrox.ASPAJAX.Samples");

//#  Song Object #
Wrox.ASPAJAX.Samples.Song = function(trackNumber,title)
{
    // Initialize as a base class.
    Wrox.ASPAJAX.Samples.Song.initializeBase(this);
    this.Title = title;
    this.TrackNumber = trackNumber;
}

//Define Album properties
Wrox.ASPAJAX.Samples.Song.prototype = {
    initialize: function() {
       Wrox.ASPAJAX.Samples.Song.callBaseMethod(this,"initialize");
    },

    get_Title: function() {
        /// <value type="String"></value>
        if (arguments.length !== 0) throw Error.parameterCount();
        return this.Title;
    },

    set_Title: function(value) {
        var e = Function._validateParams(arguments,
          [{name: "value", type: String}]);
        if (e) throw e;

        this.Title = value;
    },

    get_TrackNumber: function() {
        /// <value type="String"></value>
        if (arguments.length !== 0) throw Error.parameterCount();
        return this.TrackNumber;
    },

    set_TrackNumber: function(value) {
        var e = Function._validateParams(arguments,
          [{name: "value", type: String}]);
        if (e) throw e;
        this.TrackNumber = value;
    },

    dispose: function() {
        this.Title = null;
        this.TrackNumber = null;
        Wrox.ASPAJAX.Samples.Song.callBaseMethod(this, "dispose");
    }
}

Wrox.ASPAJAX.Samples.Song.registerClass("Wrox.ASPAJAX.Samples.Song",
  Sys.Component, Sys.IDisposable);

//#  Album Object #
Wrox.ASPAJAX.Samples.Album = function()
{
    // Initialize as a base class.
    Wrox.ASPAJAX.Samples.Album.initializeBase(this);
    this.Title;
    this.Artist;
    this.ImageUrl;
    this.Songs = [];
}

//Define Album properties
Wrox.ASPAJAX.Samples.Album.prototype = {
    initialize: function() {
       Wrox.ASPAJAX.Samples.Album.callBaseMethod(this,"initialize");
    },

    get_Title: function() {
        return this.Title;
    },

    set_Title: function(value) {
        /// <value type="String"></value>
        this.Title = value;
    },

    get_ImageUrl: function() {
        return this.ImageUrl;
    },

    set_ImageUrl: function(value) {
        /// <value type="String"></value>
        this.ImageUrl = value;
    },

    get_Artist: function() {
        return this.Artist;
    },

    set_Artist: function(value) {
        /// <value type="String"></value>
        this.Artist = value;
    },

    addSong: function(song)
    {
        /// <value type="Wrox.ASPAJAX.Samples.Song"></value>
        if (Object.getTypeName(song) != "Wrox.ASPAJAX.Samples.Song")
        {
            var e = Error.argumentType("song", Object.getType(song),
              Wrox.ASPAJAX.Samples.Song,"Wrox.ASPAJAX.Samples.Song required!");
            e.popStackFrame();
            throw e;
        }
        Array.add(this.Songs,song);
    },

    removeSong: function(song)
    {
        /// <value type="Wrox.ASPAJAX.Samples.Song"></value>
        Array.remove(this.Songs,song);
    },

    get_Songs: function()
    {
        return this.Songs;
    },

    rateSong: function(song,rating)
    {
        throw Error.notImplemented("rateSong() has not yet been implemented");
    },

    dispose: function() {
        this.Title = null;
        this.Artist = null;
        this.Songs = null;
        this.ImageUrl = null;
        Wrox.ASPAJAX.Samples.Album.callBaseMethod(this, "dispose");
    }
}

Wrox.ASPAJAX.Samples.Album.registerClass("Wrox.ASPAJAX.Samples.Album",
  Sys.Component, Sys.IDisposable);

//Added to satisfy new notifyScriptLoaded() requirement
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
Image from book

Looking through the code in Listing 11-7, you’ll see that the Album class has several properties, including Title, Artist, Songs, and ImageUrl. These properties are used by the AlbumViewer control to create new div element objects that are used to display album data. The AlbumViewer control’s set_data() method is responsible for iterating through Album and Song objects passed to the control by a page or by calls made to a web service and binding those objects to the control. It’s also responsible for clearing existing albums that are displayed when a user performs a new search and assigning styles to newly created DOM element objects.

The Album object binding process creates new div element objects as well as several others using docu- ment.createElement() and adds the div objects to the AlbumViewer control’s parent div container. set_data() also handles hooking up mouseout, mouseover, and click events for each album div object created to event handlers by using the $addHandlers() method discussed earlier. Listing 11-8 shows the complete code for the set_data() method.

Listing 11-8
Image from book

set_data: function(albums) {
    var e = this.get_element();
    //Clear any albums already showing
    while (this._albumContainer.childNodes.length > 0) {
        var child = this._albumContainer.childNodes[0];
        this._albumContainer.removeChild(child);
    }

    //Handle case where no albums are available to bind
    if (albums == null) {
        var noRecords = document.createElement("SPAN");
        noRecords.style.cssText = "font-weight:bold;";
        noRecords.innerHTML = "No albums found.";
        this._albumContainer.appendChild(noRecords);
        return;
    }

    //Loop through albums
    for (var i=0; i<albums.length; i++) {
        var album = albums[i];

        //Create Album Div
        var id = this.get_element().id + "_Album" + i.toString();
        var albumDiv = document.createElement("DIV");
        albumDiv.id = id;
        albumDiv.style.clear = "both";
        albumDiv.className = this._AlbumDivOutCssClass;
        this._albumContainer.appendChild(albumDiv);

        //Attach Events
        $addHandlers(albumDiv,
          {"mouseover" : this._overHandler,
           "mouseout" : this._outHandler,
           "click" : this._clickHandler },
           this);

        //Create album title
        var albumTitle = document.createElement("SPAN");
        albumTitle.className = this._AlbumTitleCssClass;
        albumTitle.innerHTML = album.Title;
        albumDiv.appendChild(albumTitle);

         //Create album artist
        var albumArtist = document.createElement("SPAN");
        albumArtist.className = this._AlbumArtistCssClass;
        albumArtist.innerHTML = "&nbsp;-&nbsp;" + album.Artist;
        albumDiv.appendChild(albumArtist);

        //Create child content div
        childrenDiv = document.createElement("DIV");
        childrenDiv.id = id + "_Children";
        childrenDiv.style.display = "none";
        albumDiv.appendChild(childrenDiv);

        //Create Image div
        var albumPicDiv = document.createElement("DIV");
        var img = document.createElement("IMG");
        img.src = album.ImageUrl;
        albumPicDiv.style.paddingTop = "5px";
        albumPicDiv.appendChild(img);
        childrenDiv.appendChild(albumPicDiv);

        //Create songs div
        var albumSongsDiv = document.createElement("DIV");
        var ul = document.createElement("UL");
        albumSongsDiv.appendChild(ul);

        //Loop through songs
        var songs = album.Songs;
        for (var j=0;j<songs.length;j++) {
            var li = document.createElement("LI");
            li.className = this._SongTitleCssClass;
            li.innerHTML = songs[j].Title;
            ul.appendChild(li);
        }
        childrenDiv.appendChild(albumSongsDiv);
    }
}
Image from book

Listing 11-9 shows how the AlbumViewer control’s private methods are used to handle events, locate album div nodes, assign CSS styles, and call web services. Each method is prefixed with an underscore character to convey that it’s private. Several of the methods such as _onMouseOver() and _onMouseOut handle style changes to album objects as the end user moves his or her mouse or clicks. Others perform more complex functionality such as calling web services and processing results or errors.

Listing 11-9
Image from book

_onMouseOver : function(evt) {
    this._setStyle(evt,this._AlbumDivOverCssClass);
},

_onMouseOut : function(evt) {
   this._setStyle(evt,this._AlbumDivOutCssClass);
},

_setStyle: function(evt,className) {
   evt.stopPropagation();
   this._getAlbumNode(evt.target).className = className;
   return true;
},

_getAlbumNode: function(node) {
   var baseID = this.get_element().id + "_Album";
   var currElem = node;
   while (!(currElem.id.startsWith(baseID) && !currElem.id.endsWith("_Children")))
   {
       if (currElem.parentNode) {
           currElem = currElem.parentNode;
       } else {
           break;
       }
   }
   return currElem;
},

  _onClick : function(evt) {
     var parent = this._getAlbumNode(evt.target);
     var child = $get(parent.id + "_Children");
     if (child.style.display == "block") {
         child.style.display = "none";
     } else {
         child.style.display = "block";
     }
  },

  _onButtonClick : function(evt) {
     var searchText = $get(this.get_element().id + "_txtAlbumSearch").value;
     this._invokeWebService(searchText);
  },

_onMethodComplete : function(result, userContext, methodName) {
    //Remove status label
    this._removeStatusLabel();
    // Bind returned data
    this.set_data(result);
},

_onMethodError : function(webServiceError, userContext, methodName) {
    // Call failed
    this._removeStatusLabel();
    if (webServiceError.get_timedOut()) {
        alert("Web Service call timed out.");
    } else {
        alert("Error calling Web Service: " +
          webServiceError.get_statusCode() + " " + webServiceError.get_message());
    }
},

_removeStatusLabel: function() {
    if (this._StatusLabel) {
        this.get_element().removeChild(this._StatusLabel);
        this._StatusLabel = null;
    }
},

_invokeWebService : function(searchText) {
    //Add status label in case operation takes awhile
    this._StatusLabel = document.createElement("DIV");
    this._StatusLabel.style.cssText = "font-weight:bold;color:Navy;";
    this._StatusLabel.innerHTML = "Calling Albums Web Service....";
    this.get_element().appendChild(this._StatusLabel);
    Sys.Net.WebServiceProxy.invoke(this._ServicePath, this._ServiceMethod, false,
        { prefixText:searchText, count:10 },
        Function.createDelegate(this, this._onMethodComplete),
        Function.createDelegate(this, this._onMethodError));
},

_onPropertyChanged : function(sender,args) {
    var propname = args.get_propertyName();
    if (propname == "ServicePath" || propname === "ServiceMethod") {
        var searchText = null;
        if (this._ShowSearchBox) {
            $get(this.get_element().id + "_txtAlbumSearch").value;
        }
        this._invokeWebService(searchText);
    }
}
Image from book

The _onPropertyChanged() method, shown in Listing 11-9, handles propertychanged events raised by calling the raisePropertyChanged() method discussed in the “Defining Properties” section of this chapter. Recall that _onPropertyChanged() is defined as the event handler by calling the add_propertyChanged() method in the AlbumViewer control’s initialization phase shown in Listing 11-6.

When the ServicePath or ServiceMethod properties change, _onPropertyChanged() is automatically called and used to invoke an album data web service. This allows fresh data to be loaded into the control in cases where the consumer of the control wants to dynamically change information about the web service. How do you actually call a web service in a custom control when the location of the service hasn’t been assigned to the ScriptManager’s Services property, though? Let’s take a closer look.

Calling Web Services with a Custom Control

The AlbumViewer control’s set_data() method can receive data from a client-side script or from a .NET web service. Calling a web service can be a tricky process, as different browsers having different XmlHttp objects must be taken into account. Fortunately, the ASP.NET AJAX script library has cross-browser functionality built in that makes it straightforward to make web service calls and retrieve data without worrying about the user agent.

The _invokeWebService() method shown in Listing 11-9 calls an album viewer service for the AlbumViewer control (the album web service is available in the download code for this chapter). The method accepts a single parameter that represents the album title the end user would like to search on. When called, _invokeWebService() adds a div tag to the AlbumViewer control to notify the end user that the search has begun. Because the call is made asynchronously, this provides a nice visual clue for the user so that he or she is aware that the request is being processed. The script library’s Sys.Net .WebServiceProxy class is then used to make the web service request by calling its invoke() method:

Sys.Net.WebServiceProxy.invoke(this._ServicePath, this._ServiceMethod,
  false, { prefixText:searchText, count:10 },
  Function.createDelegate(this, this._onMethodComplete),
  Function.createDelegate(this, this._onMethodError));

The invoke() method works across all major browsers and creates a JavaScript proxy, calls the web service, and processes the results. Several parameters can be passed to invoke(), including the path to the web service, the name of the Web Method to call; whether or not the call should be made using HttpGet; the parameter data to be passed to the service (parameter data is passed using JSON); a delegate that points to the callback method that handles the response that is received (_onMethodComplete()); and a delegate that points to an error handler callback method (_onMethodError()).

Once the web service is called and a response is returned, _onMethodComplete() is used to handle the response data. This method accepts one or more Album objects returned from the web service, any context data associated with the call, and the name of the Web Method that was called:

_onMethodComplete : function(result, userContext, methodName) {
    //Remove status label
    this._removeStatusLabel();
    // Bind returned data
    this.set_data(result);
}

The album data returned from the web service is bound to the AlbumViewer control by calling the set_data() method discussed earlier.

Web service calls can fail when the service is unavailable or when invalid data is passed to or from the service. Fortunately, the Sys.Net.WebServiceProxy.invoke() method used to call the service allows a callback method to be specified that handles errors. The _onMethodError() method handles any errors returned by the web service or errors generated if an invalid service path or Web Method name was used.

_onMethodError : function(webServiceError, userContext, methodName) {
    // Call failed
    this._removeStatusLabel();
    if (webServiceError.get_timedOut()) {
        alert("Web Service call timed out.");
    } else {
        alert("Error calling Web Service: " +
          webServiceError.get_statusCode() + " " +
          webServiceError.get_message());
    }
}

If an error occurs and the callback method is invoked, a Sys.Net.WebServiceError object is passed as the first parameter, along with call context data and the name of the Web Method that was initially called. Because the AlbumViewer control can’t display album data when a web service error occurs, it displays an alert to the user that contains the status code of the error as well as the error message. Before displaying error data, a check is made to ensure that the call didn’t time out by calling get_timedOut(). If the call returns true, the service was unavailable. The following table lists the properties of the Sys.Net.WebServiceError class.

Open table as spreadsheet
Property Name | Description
exception | Gets the type of exception that occurred. A string is returned.
message | Gets the error message associated with the current error.
statusCode | Gets the status code of the current HTTP response.
stackTrace | Gets the stack trace returned by the server.
timedOut | Gets a Boolean value that indicates if the web service call timed out.

Disposing of Control Resources

Because JavaScript is a garbage-collected language, many complexities associated with cleaning up variables, arrays, and other objects are greatly simplified and typically don’t require any code to be written by a developer. However, it’s important to note that specific types of resources must be explicitly cleaned up to avoid memory leaks caused by circular references. DOM event handlers used in a control, as well as processes (such as timers) that may be used internally, should be explicitly cleaned up in your custom controls.

By deriving from base classes such as Sys.Component and Sys.UI.Control, access to a method named dispose() is immediately available in custom controls that can be overridden. The AlbumViewer control uses the dispose() method shown in Listing 11-10 to cleanup event handlers and call it’s base class’s dispose() method.

Listing 11-10
Image from book

dispose: function() {
    var e = this.get_element();
    this.remove_propertyChanged(this._propertyChanged);
    if (this._ShowSearchBox) $removeHandler($get(e.id +
      "_btnAlbumSearch"),"click",this._btnClickHandler);
    //Remove event handlers
    for (var i=0;i<e.childNodes.length;i++) {
        var albumDiv = e.childNodes[i];
        $clearHandlers(albumDiv);
    }
    this._overHandler = null;
    this._outHandler = null;
    this._clickHandler = null;
    this._btnClickHandler = null;
    this._propertyChanged = null;
    Wrox.ASPAJAX.Samples.AlbumViewer.callBaseMethod(this, "dispose");
}
Image from book

In cases where all event handlers attached to a given DOM element need to be removed, the ASP.NET AJAX script library’s $clearHandlers() method can be called. When a specific event handler needs to be removed the $removeHandler() method can be called. Property changed event handlers can be removed by calling the remove_propertyChanged() method.

Once dispose() is called and event handlers are cleaned up, a call is made to the base Sys.UI.Control class’s dispose() method via callBaseMethod(), which raises a disposing event and unregisters the control from the application. Removal occurs as a result of the following calls made in the Sys.Component class’s dispose() method:

Sys.Application.unregisterDisposableObject(this);
Sys.Application.removeComponent(this);

While you don’t have to override the dispose() method on every client-side object or control used in an ASP.NET AJAX application, it’s important to override it in cases where event handlers or processes are used by a control and need to be cleaned up to prevent memory issues.

Registering a Custom Control Class

Now that you’ve seen how to create a custom control’s constructor, properties, and methods, let’s examine how the control can inherit from a base class and register itself with the ASP.NET AJAX framework. Although you can’t use the VB.NET Inherits keyword or the C# colon character to handle inheritance, you can call the registerClass() method available to all ASP.NET AJAX types through the Type class.

The registerClass() method accepts three parameters. The first parameter is a string containing the fully qualified name of the class to register with the framework. The parameter value should include the namespace and class name. The second parameter represents the base class that the control should inherit from. The third parameter specifies one or more interfaces that the control implements. This parameter is optional. Following is an example of using registerClass() to register the AlbumViewer control. Notice that the control derives from Sys.UI.Control.

Wrox.ASPAJAX.Samples.AlbumViewer.registerClass(
"Wrox.ASPAJAX.Samples.AlbumViewer", Sys.UI.Control);

As with any client-side script used by the ASP.NET AJAX framework, the AlbumViewer control adds the following line of code at the bottom the script file to notify the framework when the entire script has been loaded and is ready to be used:

if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();

Creating a Client-Side Control Instance

Once a custom client-side control has been written, it needs to be instantiated by an application. To do this, the control’s script must be referenced by the page either by using the standard script tag or by declaring it in the ScriptManager’s Scripts property. Once the control script is available for use in a page, the control can be instantiated by using the ASP.NET AJAX script library’s $create() method. $create() is an alias for the Sys.Component class’s create() method.

The $create() method accepts several different parameters as shown in the following table:

Open table as spreadsheet
Parameter | Description
type | The type of component or control that should be created.
properties | A JSON object containing properties and values that should be passed to the control. This parameter is optional.
events | A JSON object containing events and associated handlers defined through delegates. This parameter is optional.
references | A JSON object containing references to other control properties.
element | The DOM element that the control uses as its parent container. Output rendered by the control will be placed in this element. This parameter is optional.

Listing 11-11 (AlbumViewerClientBinding.aspx in the sample code) shows how the $create() method can be used to instantiate a new instance of the AlbumViewer control and hook it to a div container. When the pageLoad() event handler is called, two new Album objects are created and added to an array. The $create() method is then used to instantiate the AlbumViewer control and attach it to a div element named divAlbums. Initial CSS property values used by the control, as well as the Album objects to bind, are passed into $create() using a JSON object. Because the control’s initialize() method calls set_data(), the two albums are iterated through and added into the parent container automatically as the control initializes and loads.

Listing 11-11
Image from book

<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Album Viewer: Client-side Control</title>
    <link href="Style/Styles.css" rel="stylesheet" type="text/css" />

    <script type="text/javascript">
        function pageLoad()
        {
            //Create albums
            var album = new Wrox.ASPAJAX.Samples.Album();
            album.set_Artist("Incubus");
            album.set_Title("Light Grenades");
            album.set_ImageUrl("Images/Incubus_Light_Grenades.jpg");
            album.addSong(new Wrox.ASPAJAX.Samples.Song(1,"QuickSand"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(2,
             "A Kiss to Send Us Off"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(3,"Dig"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(4,"Anna Molly"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(5,"Love Hurts"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(6,"Light Grenades"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(7,"Earth to Bella Pt 1"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(8,"Oil and Water"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(9,"Diamonds and Coal"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(10,"Rogues"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(11,"Paper Shoes"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(12,"Earth to Bella Pt 1"));
            album.addSong(new Wrox.ASPAJAX.Samples.Song(13,"Pendulous Threads"));

            var album2 = new Wrox.ASPAJAX.Samples.Album();
            album2.set_Artist("The Killers");
            album2.set_Title("Sam's Town");
            album2.set_ImageUrl("Images/The_Killers_Sams_Town.jpg");
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(1,"Sam 's Town "));
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(2,"Enterlude"));
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(3,"When You Were Young"));
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(4,"Bling"));
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(5,"For Reasons Unknown"));
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(6,"Read My Mind"));
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(7,"Uncle Johnny"));
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(8,"Bones"));
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(9,"My List"));
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(10,"The River is Wild"));
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(11,
             "Why Do I Keep Counting"));
            album2.addSong(new Wrox.ASPAJAX.Samples.Song(12,"Exitlude"));

            var albums = [];
            Array.add(albums,album);
            Array.add(albums,album2);

            //Create AlbumViewer object and pass in albums
            $create(Wrox.ASPAJAX.Samples.AlbumViewer,
               {Albums: albums,AlbumDivOverCssClass: "AlbumDivOver",
                AlbumDivOutCssClass: "AlbumDivOut",
                AlbumTitleCssClass: "AlbumTitle",
                AlbumArtistCssClass: "AlbumArtist",
                SongTitleCssClass: "SongTitle"},
               null, //Events and handlers
               null, //References to other control properties
               $get("divAlbums"));  //parent container DOM element

        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager id="smgr" runat="Server">
        <Scripts>
            <asp:ScriptReference Path="~/Scripts/Album.js" />
            <asp:ScriptReference Path="~/Scripts/AlbumViewerStandAlone.js" />
        </Scripts>
    </asp:ScriptManager>
    <h2>Album Viewer</h2>
    <div id="divAlbums" style="width:500px;">
    </div>
    </form>
</body>
</html>
Image from book

A web service can also automatically be called when an AlbumViewer control is created to retrieve album data, rather than creating the album objects in the page as in Listing 11-11. This is done by passing the path to the web service along with the name of the Web Method to call in the JSON object that contains properties and values. An example of using the $create() method to pass ServicePath and ServiceMethod property values to the control is shown in Listing 11-12 (AlbumViewerWSBinding.aspx in the sample code). The ShowSearchBox property is also set to a value of true so that the album search textbox and button controls are created and shown. The output generated by the control is shown in Figure 11-1.

Listing 11-12
Image from book

function pageLoad()
{
     $create(Wrox.ASPAJAX.Samples.AlbumViewer,
        {ShowSearchBox: true,AlbumDivOverCssClass: "AlbumDivOver",
         AlbumDivOutCssClass: "AlbumDivOut",AlbumTitleCssClass: "AlbumTitle",
         AlbumArtistCssClass: "AlbumArtist",SongTitleCssClass: "SongTitle",
         ServicePath: "AlbumService.asmx",ServiceMethod: "GetAlbums"},
        null,
        null,
        $get("divAlbums"));
}
Image from book

Building a Server-Side ASP.NET AJAX Control

Creating custom ASP.NET AJAX client-side controls is a fairly intensive exercise in JavaScript. Calling control methods, handling events, and even instantiating the control itself requires JavaScript code to be written. While some ASP.NET developers understand how to write JavaScript code, others may not have a complete understanding of the language and may prefer to work with server-side objects that can be manipulated using VB.NET or C#. They may prefer to be shielded from JavaScript code whenever possible.

Because server-side controls can output both HTML and JavaScript, they’re good candidates for use with ASP.NET AJAX client-side controls. Developers familiar with defining and interacting with server-side controls can use an AJAX-enabled server-side control without having to worry about the client-side complexities. A nice "feature" of ASP.NET AJAX server-side controls is that once you have created the client-side script that handles all of the HTML output and event handling (as with the AlbumViewer control shown in this chapter), going a little further to create a server-side control is fairly straightforward and requires very little VB.NET or C# code. After all, most of the control’s functionality is performed by the client-side script.

There are a few different paths to creating an ASP.NET AJAX server control. To choose the correct path, you need to decide what type of control you want to create. Will the control be visual, or is it something that runs in the background like a timer? Will the control extend functionality of another control? The answer to these questions determines how the server-side control is built. For example, if you want to extend an existing control’s functionality by adding AJAX capabilities to it, you’ll want to derive from the System.Web.UI.ExtenderControl class. If you’re writing a visual control from scratch that generates HTML and JavaScript, you’ll want to derive from System.Web.UI.WebControl and implement the IScriptControl interface.

The AlbumViewer server-control discussed in the remainder of this chapter derives from WebControl, implements the IScriptControl interface, and leverages the AlbumViewer client-side control script discussed earlier in this chapter. It lives in an ASP.NET control library project within Visual Studio .NET 2005. Before getting into specifics about the server-side control, let’s examine how to embed a client-side control script into a server-side control assembly.

Embedding Scripts in Server-Side Controls

ASP.NET AJAX server-side controls perform two main purposes. First, they output a client-side control script to the browser that handles the bulk of the functionality required by the control. This includes interaction with the end user, event handling, data binding, calls to remote data source, and more. Second, they define properties that can be set on the server-side and passed to the client-side script. This allows ASP.NET developers to define properties using standard ASP.NET control syntax without worrying about writing custom JavaScript code or even using the ASP.NET AJAX script library’s $create() method to instantiate the control.

Client-side ASP.NET AJAX control scripts can be embedded directly in ASP.NET server control assemblies by marking the scripts as an embedded resource. First, add a script file to an ASP.NET server control project (officially called a Class Library project in Visual Studio .NET 2005) and add all of the client-side functionality to it. Next, right-click the script in the Visual Studio .NET 2005 Solution Explorer, select Properties and set the Build Action property to a value of Embedded Resource. This will cause the compiler to embed the script file directly in the control’s assembly. Figure 11-2 shows the Properties window for the embedded script file.

Image from book
Figure 11-2

To make the script accessible, add the following assembly attribute immediately above the server con-trol’s namespace definition in code file (C# is shown below):

[assembly: System.Web.UI.WebResource("AlbumViewer.AlbumViewer.js",
  "text/javascript")]

This makes the script accessible through an HttpHandler built into ASP.NET AJAX called ScriptResource.axd and avoids having to deploy the physical script file along with the server control assembly, which can greatly simplify deployment and maintenance. The WebResource attribute’s constructor accepts the name of the resource followed by the content-type of the resource. The name given to the resource is the assembly name followed by the name of the script (AlbumViewer.AlbumViewer.js, in this case). Although JavaScript is used in this example, images can also be embedded in custom server controls using the same technique. One disadvantage of embedding scripts or images into assemblies is that they can’t be modified on the web server; you would have to rebuild the assembly with the modified objects and redeploy it to the web server.

Creating a Control Class and Implementing IScriptControl

The AlbumViewer ASP.NET AJAX server-side control allows a developer to set properties on the control using familiar ASP.NET server control syntax. After property values are set, the control handles out-putting the client-side control script that’s embedded as a resource and creates a new instance of the control using the $create() method. Property values set on the server side are passed to the $create() method through a JSON object that is passed as a parameter to $create().

Because the AlbumViewer server-side control doesn’t extend an existing control, it inherits from System.Web.UI.WebControl. Had the control extended an existing control, such as a Textbox, it would inherit from System.Web.UI.ExtenderControl located in the System.Web.Extensions.dll assembly that’s installed with the ASP.NET AJAX framework. In addition to inheriting from WebControl, the AlbumViewer control also implements the IScriptControl interface located in the System.Web.UI namespace of the System.Web.Extensions.dll assembly. The IScriptControl interface defines two methods, shown in the following table:

Open table as spreadsheet
Method | Description
GetScriptDescriptors | Gets a collection of script descriptors that represent JavaScript components on the client. This method is used to map server-side control properties to client-side control properties.
GetScriptReferences | Gets a collection of ScriptReference objects that define script resources required by the control. This method is used to identify one or more embedded script resources in a control assembly and output them to the client.

Listing 11-13 shows the shell for the AlbumViewer server-side control. Notice that it derives from WebControl and implements IScriptControl.

Listing 11-13
Image from book

using System;
using System.Configuration;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

//Client-side script file embedded in assembly
[assembly: System.Web.UI.WebResource("AlbumViewer.AlbumViewer.js",
  "text/javascript")]
namespace Wrox.ASPAJAX.Samples
{
    public class AlbumViewer : WebControl, IScriptControl
    {

        //Properties and methods

        IEnumerable<ScriptReference> IScriptControl.GetScriptReferences()
        {
            return GetScriptReferences();
        }

        IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()
        {
            return GetScriptDescriptors();
        }

    }
}
Image from book

The two implemented methods shown in Listing 11-13 satisfy the IScriptControl interface and delegate processing to two additional methods included in the class named GetScriptReferences() and GetScriptDescriptors(). These two methods are shown in Listing 11-14.

Listing 11-14
Image from book

protected virtual IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
    ScriptControlDescriptor descriptor =
      new ScriptControlDescriptor("Wrox.ASPAJAX.Samples.AlbumViewer",
      this.ClientID);
    descriptor.AddProperty("AlbumDivOverCssClass", this.AlbumDivOverCssClass);
    descriptor.AddProperty("AlbumDivOutCssClass", this.AlbumDivOutCssClass);
    descriptor.AddProperty("AlbumTitleCssClass", this.AlbumTitleCssClass);
    descriptor.AddProperty("AlbumArtistCssClass", this.AlbumArtistCssClass);
    descriptor.AddProperty("SongTitleCssClass", this.SongTitleCssClass);
    descriptor.AddProperty("ServicePath", this.ServicePath);
    descriptor.AddProperty("ServiceMethod", this.ServiceMethod);
    descriptor.AddProperty("ShowSearchBox", this.ShowSearchBox);
    //could also use: yield return descriptor;
    return new ScriptDescriptor[] { descriptor };
}

protected virtual IEnumerable<ScriptReference> GetScriptReferences()
{
    ScriptReference r = new ScriptReference();
    r.Assembly = "AlbumViewer";
    r.Name = "AlbumViewer.AlbumViewer.js";
    //could also use:  yield return r;
    return new ScriptReference[] { r };
}
Image from book

The GetScriptReferences() method shown in Listing 11-14 creates a ScriptReference object and identifies the script resource that the server control needs to send to the client (AlbumViewer.AlbumViewer.js, in this case). The GetScriptDescriptors() method creates a new ScriptControlDescriptor object that maps server-side control properties to client-side control properties. This is necessary so that the property values set on the control by a developer can be passed to the $create() method used to instantiate the client-side control. The properties defined in the AlbumViewer server-side control are the same ones discussed earlier in the chapter for the client-side control (AlbumDivOverCssClass, AlbumDivOutCssClass, and so on).

Overriding Render and OnPreRender

Once the AlbumViewer control’s script resources are identified and server-side properties are mapped to client-side properties, the control overrides the OnPreRender() method of WebControl and retrieves a reference to the ScriptManager object in the page. The ScriptManager object is then used to register the AlbumViewer control as a script control by calling the ScriptManager’s RegisterScriptControl() method.

The AlbumViewer server-side control also overrides the Render() method of WebControl to register the property mappings (referred to as script descriptors) returned from the GetScriptDescriptors() method. This serves the purpose of passing the control’s property values defined on the server-side to the $create() method generated automatically by the ScriptManager control. Listing 11-15 shows the OnPreRender() and Render() methods.

Listing 11-15
Image from book

ScriptManager _ScriptManager;

protected override void OnPreRender(EventArgs e)
{
    if (!this.DesignMode)
    {
        _ScriptManager = ScriptManager.GetCurrent(Page);

        if (_ScriptManager == null)
            throw new HttpException("A ScriptManager control is required " +
            "in the page.");

        _ScriptManager.RegisterScriptControl(this);
    }

    base.OnPreRender(e);
}

protected override void Render(HtmlTextWriter writer)
{
    if (!this.DesignMode)
    {
        _ScriptManager.RegisterScriptDescriptors(this);
    }
    base.Render(writer);
}
Image from book

By registering itself with the ScriptManager control, the script associated with the custom AlbumViewer control will automatically be referenced in the page where the control is used. A call to the client-side $create() method will also be added automatically into the page as it renders. This allows developers to use the AlbumViewer control without writing a single line of JavaScript yet benefit from all of the client-side functionality provided by the control.

Listing 11-16 shows the complete code for the AlbumViewer server-side control.

Listing 11-16
Image from book

using System;
using System.Configuration;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

//Client-side script file embedded in assembly
[assembly: System.Web.UI.WebResource("AlbumViewer.AlbumViewer.js",
  "text/javascript")]
namespace Wrox.ASPAJAX.Samples
{
    public class AlbumViewer : WebControl, IScriptControl
    {
        string _AlbumDivOverCssClass;
        string _AlbumDivOutCssClass;
        string _AlbumTitleCssClass;
        string _AlbumArtistCssClass;
        string _SongTitleCssClass;
        string _ServicePath;
        string _ServiceMethod;
        bool _ShowSearchBox;
        ScriptManager _ScriptManager;

        #region Properties

        public string AlbumDivOverCssClass
{
    get { return _AlbumDivOverCssClass; }
    set { _AlbumDivOverCssClass = value; }
}

public string AlbumDivOutCssClass
{
    get { return _AlbumDivOutCssClass; }
    set { _AlbumDivOutCssClass = value; }
}

public string AlbumTitleCssClass
{
    get { return _AlbumTitleCssClass; }
    set { _AlbumTitleCssClass = value; }
}

public string AlbumArtistCssClass
{
    get { return _AlbumArtistCssClass; }
    set { _AlbumArtistCssClass = value; }
}

public string SongTitleCssClass
{
    get { return _SongTitleCssClass; }
    set { _SongTitleCssClass = value; }
}

public string ServicePath
{
    get { return _ServicePath; }
    set { _ServicePath = value; }
}

public string ServiceMethod
{
    get { return _ServiceMethod; }
    set { _ServiceMethod = value; }
}

public bool ShowSearchBox
{
    get { return _ShowSearchBox; }
    set { _ShowSearchBox = value; }
}

#endregion

public AlbumViewer() {}

protected override void OnPreRender(EventArgs e)
{
    if (!this.DesignMode)
    {
        _ScriptManager = ScriptManager.GetCurrent(Page);

        if (_ScriptManager == null)
            throw new HttpException("A ScriptManager control is " +
            "required in the page.");

        _ScriptManager.RegisterScriptControl(this);
    }

    base.OnPreRender(e);
}

protected override void Render(HtmlTextWriter writer)
{
    if (!this.DesignMode)
    {
        _ScriptManager.RegisterScriptDescriptors(this);
    }
    base.Render(writer);
}

protected virtual IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
    ScriptControlDescriptor descriptor =
      new ScriptControlDescriptor("Wrox.ASPAJAX.Samples.AlbumViewer",
      this.ClientID);
    descriptor.AddProperty("AlbumDivOverCssClass",
      this.AlbumDivOverCssClass);
    descriptor.AddProperty("AlbumDivOutCssClass",
      this.AlbumDivOutCssClass);
    descriptor.AddProperty("AlbumTitleCssClass", this.AlbumTitleCssClass);
    descriptor.AddProperty("AlbumArtistCssClass",
      this.AlbumArtistCssClass);
    descriptor.AddProperty("SongTitleCssClass", this.SongTitleCssClass);
    descriptor.AddProperty("ServicePath", this.ServicePath);
    descriptor.AddProperty("ServiceMethod", this.ServiceMethod);
    descriptor.AddProperty("ShowSearchBox", this.ShowSearchBox);
    //could also use: yield return descriptor;
    return new ScriptDescriptor[] { descriptor };
}

protected virtual IEnumerable<ScriptReference> GetScriptReferences()
{
    ScriptReference r = new ScriptReference();
    //Use following when client-side script
    //embedded in custom control:
    //reference.Path = this.ResolveClientUrl("~/Scripts/AlbumViewer.js");
    r.Assembly = "AlbumViewer";
    r.Name = "AlbumViewer.AlbumViewer.js";
    //could also use:  yield return r;
    return new ScriptReference[] { r };
        }
        IEnumerable<ScriptReference> IScriptControl.GetScriptReferences()
        {
            return GetScriptReferences();
        }

        IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()
        {
            return GetScriptDescriptors();
        }

    }
}
Image from book

Now that you’ve seen the pieces required to build a custom ASP.NET AJAX server control, let’s examine how to use one in an ASP.NET page.

Using a Custom ASP.NET AJAX Control in an ASP.NET Page

Once the AlbumViewer server-side control project is compiled and the generated assembly is referenced by an ASP.NET website project, the control can be registered in an ASP.NET page by using the Register directive:

<%@ Register Assembly="AlbumViewer" Namespace="Wrox.ASPAJAX.Samples"
    TagPrefix="ajax" %>

This directive references the assembly name (AlbumViewer), the namespace that the control resides in (Wrox.ASPAJAX.Samples), as well as the tag prefix that should be used to define the control in the ASP.NET page (ajax). Once registered, this control can be used in the page just the same as any other ASP.NET server control. You merely need to define the tag prefix and class name followed by all of the necessary properties in the Register directive. An example of adding the AlbumViewer control into a page is shown as follows:

<ajax:AlbumViewer ID="divAlbums" runat="server" ShowSearchBox="true"
  AlbumDivOverCssClass="AlbumDivOver"
  AlbumDivOutCssClass="AlbumDivOut" AlbumTitleCssClass="AlbumTitle"
  AlbumArtistCssClass="AlbumArtist" SongTitleCssClass="SongTitle"
  ServicePath="AlbumService.asmx" ServiceMethod="GetAlbums" />

Once the page is called by an end user through a browser, the necessary client-side script code used by the AlbumViewer control will automatically be added to the rendered output by the ScriptManager control. A portion of the output generated by the ScriptManager that instantiates the AlbumViewer control is shown in Listing 11-17.

Listing 11-17
Image from book

<script type="text/javascript">
<!-
    Sys.Application.initialize();
    Sys.Application.add_init(function() {
        $create(Wrox.ASPAJAX.Samples.AlbumViewer,
          {"AlbumArtistCssClass":"AlbumArtist",
           "AlbumDivOutCssClass":"AlbumDivOut",
           "AlbumDivOverCssClass":"AlbumDivOver",
           "AlbumTitleCssClass":"AlbumTitle",
           "ServiceMethod":"GetAlbums",
           "ServicePath":"AlbumService.asmx",
           "ShowSearchBox":true,
           "SongTitleCssClass":"SongTitle"},
           null, null,
           $get("divAlbums"));
        });
// -->
</script>
Image from book

The AlbumViewer.js script embedded in the control is automatically referenced through a script tag embedded into the page. The script tag’s src attribute points to a ScriptResource.axd HttpHandler that will extract the script from the server-side assembly where it was embedded and send it down to the browser:

<script
  src="/Chapter11/ScriptResource.axd?d=eyWA0k6p8cJT5ZxERTSqjh74z_7LL
       XVOadc8XgiZ-JKa85A2IV0dD9FkMF5UiS40&amp;t=633048513771318000"
  type="text/javascript">
</script>


Summary

Custom ASP.NET AJAX controls allow specialized functionality to be added into an application that can be reused over and over. In this chapter, you’ve seen how to create a client-side ASP.NET AJAX control capable of generating custom output. Core steps that should be followed when creating controls were discussed, including making a constructor, using the prototype design pattern, initializing controls, and disposing of control resources. You’ve also seen how control events can be hooked up to event handlers and how controls can be created and used in an application.

This chapter also discussed how to create custom server-side ASP.NET AJAX controls. Server-side controls can be created to extend existing controls, or new controls can be built from the ground up that add unique features and functionality. You’ve seen how the IScriptControl interface can be used and how scripts and properties used by a client-side script can be referenced and used. In addition to examining these topics, you’ve also seen how a custom control can be registered as a script control with the ScriptManager. Finally, you’ve seen how a custom server-side control can be defined and used in a page.


Index

$addHandler method, 268
$create method, 280
* (asterisk), anonymous users, 164
= (equals sign)
logical test, 50
? (question mark), anonymous users, 164

A

accessibility
AJAX and, 41
Accordion control, 186 , 188
Accordion.aspx code listing (8-1), 186 , 188
ActivateTabChanged event, 195
ActiveXObject function, 131
add completedRequest method, WebRequestManager, 146
add method, 90
add propertyChanged method, 268
AJAX
accessibility and, 41
Asynchronous JavaScript and XML, 2
developers and, 3, 5
end users and, 3
JavaScript libraries and, 6
AJAX Library, 49
as standalone .zip file, 241
namespaces and, 73
page lifecycle, 72
prototype object, 60
reflection functions, 78
ScriptManager and, 103
AJAX Toolkit
Accordion control, 186, 188
AlwaysVisibleControlExtender control, 189, 191
CollapsiblePanelExtender control, 191
introduction, 185
ResizableControl control, 192, 194
TabContainer control, 195
TabPanel control, 195
UI effects
animations, 197, 198
DropShadow, 200
RoundedCorners, 201
UpdatePanelAnimation, 199, 200
Ajax.NET Professional, 13
Album.asmx code listing (6-17), 153 , 155
Album.cs code listing (7-5), 173
AlbumViewerClientBinding.aspx code listing (11-11), 281 , 283
AlbumViewerWSBinding.aspx code listing (11-12), 283
AlwaysVisibleControl.aspx code listing (8-2), 190
AlwaysVisibleControlExtender control, 189 , 191
animation
events, 197
Animation.aspx code listing (8-6), 197 , 198
AnimationExtender, 197
animations, 197 , 198
UpdatePanelAnimation, 199, 200
anonymous users, * (asterisk) and, 164
anonymous users, ? (question mark) and, 164
anonymousIdentification element, 123
Anthem.NET, 13
application services
users
authenticating, 163, 164, 166, 167, 169, 171
Array type, 89 , 91
arrays, 11
JSON, 149
Arrays.aspx code listing (4-16), 90 , 91
ASP (Active Server Pages), 8
ASP.NET AJAX, 6 , 7
installation, 7
ASP.NET AJAX Control Toolkit, 238 , 239
ASP.NET AJAX Library
Error class, 212, 214, 215
Sys.Debug class, 215
assert operations, 220
trace operations, 216, 218
assert function, 220
assert operations
Sys.Debug class, 220
AssignmentIsTrue.aspx code listing (3-6), 50 , 51
asterisk (*), anonymous users, 164
asynchronous
definition, 18
asynchronous postback
error handling and, 127
AsyncPostBackErrorMessage, 127
AsyncPostBackTimeout property, 128
attributes
defer, 44
language, 44
ScriptIgnore, 159
ScriptMethod, 157
ScriptService, 117
WebMethod, 157
authentication
custom, 166
Forms authentication, 164
JavaScript, 167, 169
Passport accounts, 164
users, 163, 164, 166, 167, 169, 171
ValidateUser method, 166
Windows accounts, 164
Authentication service
enabling, 167
user's credentials, 167
authentication services
Forms Authentication, 119, 120, 121
Authentication.asmx code listing (5-12), 121 , 122
AuthenticationService element, 121


Previous Page
Next Page

Index

B

Bare.aspx code listing (5-1), 103
Base Class Library
Array type, 89, 91
dates, 88
numbers, 88
string class, 87
base methods
implementation
calling, 79
BasicUpdatePanel.aspx code listing, 19
BasicUpdatePanel.aspx code listing (2-1), 18
boolean.aspx code listing (3-3), 47 , 48
Booleans, 9 , 47
browser
postback and, 17
Browser object, 99
Browser.aspx code listing (4-23), 99 , 100
browsers
JavaScript and, 44
Profile data, accessing, 175, 176
buttons
Submit
disabling, 45


Previous Page
Next Page

Index

C

cache variables, 69
Calendar.aspx code listing (8-9), 202 , 203
CalendarExtender, 202
CallingUpdate.aspx code listing (2-3), 24 , 25
CallTime.aspx code listing (6-2), 133 , 134
CallTimeWebRequest.aspx code listing (6-3), 134 , 135
camel-casing, member variables, 47
CancelEventArgs
invokingRequest handler and, 147
CascadingDropDownExtender, 206 , 208
case sensitivity, 47
catch blocks, 66
CGI (Common Gateway Interface), 8
Class.aspx code listing (4-4), 75 , 76
classes
control
registering, 280
control classes, 285, 287
creating, 75, 77, 78, 79, 80
Debug, 95
Error, 212, 214, 215
JavaScriptSerializer, 155
Sys.Debug, 215, 216, 218, 220
Type, 256
clear method, 90
clearTrace function, 218
client-side controls
creating instances, 280, 281, 283
extending JavaScript, 256, 257, 259
ClientLifecycle.aspx (2-8), 33 , 34 296
ClientScriptManager object, 129
closures, 60 , 61 , 63 , 64
DOM elements, 64
garbage collection and, 64
inner functions, 61
memory leaks, 64
code listings
Accordion.aspx (8-1), 186, 188
Album.asmx (6-17), 153, 155
Album.cs (7-5), 173
AlbumViewerClientBinding.aspx (11-11), 281, 283
AlbumViewerWSBinding.aspx (11-12), 283
AlwaysVisibleControl.aspx (8-2), 190
Animation.aspx (8-6-), 197, 198
Arrays.apsx (4-16), 90, 91
AssignmentIsTrue.aspx (3-6), 50, 51
Authentication.asmx (5-12), 121, 122
Bare.aspx (5-1), 103
BasicUpdatePanel.aspx, 19
BasicUpdatePanel.aspx (2-1), 18
boolean.aspx (3-3), 47, 48
Browser.aspx (4-23), 99, 100
Calendar.aspx (8-9), 202, 203
CallingUpdate.aspx (2-3), 24, 25
CallTime.aspx (6-2), 133, 134
CallTimeWebRequest.aspx (6-3), 134, 135
Class.aspx (4-4), 75, 76
ClientLifecycle.aspx (2-8), 33, 34
CollapsiblePanel.aspx (8-3), 191, 192
ConfirmButton.aspx (8-10), 204
CreateFunction.aspx (3-5), 49, 50
CustomHeaders.aspx (6-8), 139, 140
EmbeddedReference.aspx (5-5), 110
embeddedSample.debug.js (5-4), 109
Enable.htm (3-2), 45
Equality.aspx (3-7), 51, 52
ErrorTypes.aspx (4-22), 97, 98
Eval.aspx (6-16), 151, 152
eval.htm (3-17), 65
Exceptions.aspx (3-18), 66, 67
Flags.aspx (4-13), 86, 87
GlobalCompleted.aspx (6-13), 146
Globalization.aspx (5-7), 115
Inequality.aspx (3-8), 53
Intellisense.aspx (4-18), 92, 93
LifecycleCounts.aspx (2-9), 35, 37
LoadProfileItems.aspx (7-8), 179, 180
LocalizedResource.aspx (5-6), 113
Login.aspx (5-11), 120
Logout.aspx (7-4), 170, 171
Objects.aspx (4-17), 91
PageMethods.aspx (6-19), 157, 158
PreLoadedProfileItems.aspx (7-7), 177
Profile.aspx (5-13), 123, 124
Profile.aspx (7-6), 174, 175
RoundedCorners.aspx (8-8), 201, 202
Sample.js (5-3), 106
SaveProfileItems.aspx (7-9), 181, 183
ScriptProxy.aspx (6-18), 155, 157
ScriptReference.aspx (5-2), 105
Servertime.asmx (5-8), 116
ServiceReference.aspx (5-9), 117
SetDefaultTimout.aspx (6-12), 144, 145
SetVerb.aspx (6-5), 136
SingleTimer.aspx (2-7), 32, 33
Status.aspx (7-3), 169
Tabs.aspx (8-5), 195, 197
Time.aspx (6-1), 133
TimedSleep.aspx (6-10), 143
Timeout.aspx (6-6), 137, 138
Timer.aspx (2-6), 31, 32
TraceConsole.aspx (4-21), 95
Trim.aspx (4-14), 87, 88
UpdatePanelAnimation.aspx (8-7), 199, 200
UpdateProgress.aspx (2-5), 28
UpdateProgress.aspx.cs (2-4), 27
UserContext.aspx (6-9), 140, 142
XMLHttpExecutor.aspx (6-15), 148
XMLTime.aspx (6-14), 147
CodeDOM (code document object model), 65
CollapsiblePanel.aspx code listing (8-3), 191 , 192
CollapsiblePanelExtender control, 191
compression
enabling, 69
scripts, 129
dynamic scripts, 251, 252
static scripts, 252
ConfirmButton.aspx code listing (8-10), 204
ConfirmButtonExtender, 203 , 204
constructors
controls
creating, 259, 261
contains method, 90
controls
classes
registering, 280
client-side
creating instances, 280, 281, 283
extending JavaScript, 256, 257, 259
constructors
creating, 259, 261
custom
web services and, 277, 278
initializing
event handling and, 266, 268, 269
methods, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278
namespaces
registering, 259
pop-up
CalendarExtender, 202
CascadingDropDownExtender, 206, 208
ConfirmButtonExtender, 203, 204
HoverMenuExtender, 204
ModalPopupExtender, 205
PopupControlExtender, 205
properties, 263, 264, 266
resources
disposing of, 279, 280
server-side, 283
Render method, 287, 288, 289, 291
control classes, 285, 287
embedding scripts in, 284, 285
OnPreRender method, 287, 288, 289, 291
registering in ASP.NET page, 291, 292
Timer, 31, 32, 33
Type class, 257
UpdatePanel, 17, 19, 20, 22, 23, 24, 26, 27
UpdateProgress, 27, 28
validation, 26
cookies, 120
create method
Error object, 96, 98
CreateFunction function, 49
CreateFunction.aspx code listing (3-5), 49 , 50
Culture property, 114
CultureInfo object, 114
CustomHeaders.aspx code listing (6-8), 139 , 140


Previous Page
Next Page

Index

D

dates
Base Class library, 88
JSON, 158
Debug class, 95
debug scripts, 209 , 210 , 211 , 212
Debug Trace Console, 95
debugging, 95
disabling during deployment, 242
Error object, 96, 98
Firefox and
Firebug, 228, 230
Internet Explorer and, 221, 222
Microsoft Script Debugger, 226, 227
Visual Studio .NET 2005, 222, 224, 225, 226
declaring namespaces, 73 , 74
declaring variables
using without, 54
defer attribute, 44
deployment
ASP.NET AJAX Control Toolkit, 238, 239
configuration problems, 242, 243, 244, 245
debugging, disabling, 242
errors, custom, 243
Extensions and, 238
Futures Community Technical Preview (CTP), 239
platforms, 241
setting deployment mode, 245
tracing, disabling, 243, 244
Web Deployment Project, 245, 248
web farm
MachineKey and, 240
session state and, 240, 241
Deserialize method, JavaScriptConverter, 160
dispose method, 279
disposing of control resources, 279 , 280
document.createElement method, 273
DoJo toolkit, 13
DOM
closures and, 64
CodeDOM, 65
DOM (Document Object Model)
event handlers, 39
Firebug and, 228
DropDownList, 206
DropShadow, 200
dynamic HTML, 12
dynamic languages, 9
dynamic scripts
compression, 251, 252


Previous Page
Next Page

Index

E

EcmaScript, 9
EmbeddedReference.aspx code listing (5-5), 110
embeddedSample.debug.js code listing (5-4), 109
embedding
ScriptResources, 108, 109, 111
Enable.htm code listing (3-2), 45
EnableGlobalization, 114
EnablePageMethods property
ScriptManager, 157
endRequest event handler, 126
EndRequestEventArgs, 38
enumerations
declaring, 73
enums, 83
flags, 84
supporting, 85
values, 83
equality, 50 , 51
Equality.aspx code listing (3-7), 51 , 52
Error class, 212 , 214 , 215
functions, 212
error detection
page requests, 38
error handling
asynchronous postback and, 127
custom errors, 126, 127
Error object, 96 , 98
errors
creating, 96, 98
custom, deployment and, 243
ErrorTypes.aspx code listing (4-22), 97 , 98
eval function, 65
code speed, 66
eval statement, 65
Eval.aspx code listing (6-16), 151 , 152
event handlers
DOM elements, 39
endRequest, 126
event handling
initializing controls and, 266, 268, 269
events
ActivateTabChanged, 195
animation events, 197
OnClick, 197
OnClientActiveTabChanged, 195
OnHoverOut, 197
OnHoverOver, 197
OnLoad, 197
OnMouseOut, 197
OnMouseOver, 197
ReadyStateChange, 133
ResolveScriptReference, 129
exception handling, 66
Exceptions
aspx (3-18), 66, 67
executeRequest method, WebRequestManager, 146
Extensions
deployment and, 238
GAC (Global Assembly Cache) and, 238


Previous Page
Next Page

Index

F

FadeTransitions property, 186
fail function, 224
Fiddler
Internet Explorer and, 231
messages, 231, 232, 234
finally blocks, 66
Firefox
debugging
Firebug and, 228, 230
flags, 84
types
registering, 84
Flags.aspx code listing (4-13), 86 , 87
format method, 88
Forms Authentication, 119 , 120 , 121 , 164
configuration, 164, 166
cookies, 120
function
pageUnload, 72
function keyword, 259
Function object
validateParams method, 99
Function.createDelegate method, 268
functions, 9 , 47
ActiveXObject, 131
assert, 220
clearTrace, 218
CreateFunction, 49
Error class, 212
eval, 65
fail, 224
get isLoggedIn, 169
get timedout, 137
getInstance, 33
getInterfaces, 81
initializeBase, 77
inner functions
closures, 61
isImplementedBy, 81
pageLoad, 72, 168
readyStateChangeHandler, 133
reflection functions, 78
registerClass, 76, 84
registerNamespace, 259
substr, 68
substring, 68
toString, 84
traceDump, 218
Type.getRootnamespaces, 74
Type.isNamespace, 74
Type.registerNamespace, 74
Futures Community Technical Preview (CTP), 239


Previous Page
Next Page

Index

G

GAC (Global Assembly Cache), 238
garbage collection, 64
get isLoggedIn function, 169
GET request, 135
get timedout function, 137
get webRequest method
NetworkRequestEventArgs, 147
getInstance function, 33
getInterfaces function, 81
GetScriptDescriptors method, 287
GetScriptReferences method, 287
global WebRequest handling, 145 , 147
GlobalCompleted.aspx code listing (6-13), 146
globalization
scripts, 114
Globalization.aspx code listing (5.7), 115


Previous Page
Next Page

Index

H

HEAD verb, 135
HoverMenuExtender, 204
HTML
dynamic HTML, 12
HTTP headers
custom, 138, 139, 140
getResponseHeader and, 132
setRequestHeader and, 132
HTTP requests
GET, 135
verbs, 135, 136


Previous Page
Next Page

Index

I

IIS7
configuration, 250, 251
implementsInterface method, 81
Inequality.aspx code listing (3-8), 53
inheritance
JavaScript and, 58
types, 73
initializeBase function, 77
initializeBase method, 260
initializeRequest event, 33 , 34
initializing controls
event handling and, 266, 268, 269
InlineScript property, 117
inner functions
closures, 61
Intellisense, 92 , 93 , 94
Intellisense.aspx code listing (4-18), 92 , 93
interfaces, 80
definitions, 73, 80
IProvideTrackInfo, 81
IScriptControl, 285, 287
Internet Explorer
debugging, 221, 222
Microsoft Script Debugger, 226, 227
Visual Studio 2005, 222, 224, 225, 226
Fiddler and, 231
Internet Services Manager
compression
enabling, 69
_invokeWebService method, 277
IprovideTrackInfo interface, 81
ISAPI (Internet Server API), 8
IScriptControl interface, 285 , 287
isImplementedBy function, 81


Previous Page
Next Page

Index

J

JavaScript, 8 , 9 , 11
as text resource, 46
authentication in, 167, 169
Booleans, 9
browsers and, 44
functions, 9
inheritance and, 58
numbers, 9
objects
creating, 131
pages and, 44
script
reducing, 69
strings, 9
type definitions, 58
variables, 9
JavaScriptConvert
custom serialization and, 160
JavaScriptConverter
custom converter, 160
Deserialize method, 160
Serialize method, 160
SupportedTypes method, 160
JavaScriptSerializer
custom serialization, 160
JavaScriptSerializer class, 155
JAWS screen reader, 41
JSON
arrays, 149
dates, 158
objects, 149
ordered lists, 149
prototype design pattern, 261, 262, 263
serialization, 153, 155
serialization and, 149
serializer
configuration, 160
unordered key-value pairs, 149
JSON (JavaScript Object Notation), 148


Previous Page
Next Page

Index

K

keywords
function, 259
new, 131
Kothari, Nikhil, 234


Previous Page
Next Page

Index

L

language attribute, 44
libraries
Ajax.NET Professional, 13
Anthem.NET, 13
DoJo, 13
Prototype, 13
Rico, 13
Script.aculo.us, 13
LifecycleCounts.aspx code listing (2-9), 35 , 37
LoadProfileItems.aspx code listing (7-8), 179 , 180
localeFormat method, 88
localization
scripts, 111, 113, 114
LocalizedResource.aspx code listing (5-6), 113
logged in status of users, 169
Login.aspx code listing (5-11), 120
logout function
redirectUrl parameter, 170, 171
Logout.aspx code listing (7-4), 170 , 171


Previous Page
Next Page

Index

M

MachineKey
web farm deployment and, 240
mashup applications, 132
member variables
camel-casing, 47
memory
garbage collection and, 64
memory leaks, 64
messages
Fiddler and, 231, 232, 234
Web Developmer Helper and, 234, 236
methods
$addHandler, 268
$create, 280
add, 90
add propertyChanged, 268
clear, 90
contains, 90
controls, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278
dispose, 279
document.createElement, 273
format, 88
Function.createDelegate, 268
GetScriptDescriptors, 287
GetSriptReferences, 287
implementsInterface, 81
initializeBase, 260
invoking as SOAP service, 155
localeFormat, 88
OnPreRender, 287, 288, 289, 291
raisePropertyChanged, 266
registerClass, 280
registerInterface, 81
Render, 287, 288, 289, 291
set_data, 273
StrangeCaseIt, 59
substring, 68
Update, 24
validateParams, 99
ValidateUser, 166
XMLHttpRequest object, 132, 134
_invokeWebService, 277
_onPropertyChanged, 277
Microsoft Script Debugger
Internet Explorer
debugging and, 226, 227
ModalPopupExtender, 205


Previous Page
Next Page

Index

N

namespaces
AJAX Library and, 73
code organization and, 74
controls
registering, 259
declaring, 73, 74
loading, 73
objects, 73
support, 73
System.Graphics, 73
System.Web, 73
NetworkRequestEventArgs
get webRequest method, 147
new keyword, 131
null
undefined and, 52, 54
numbers, 9 , 47
Base Class library, 88


Previous Page
Next Page

Index

O

object
Error, 96, 98
objects, 11
Browser, 99
ClientScriptManager, 129
creating
in JavaScript, 131
CultureInfo, 114
JSON, 149
namespaces, 73
PageMethods, 157
ProfileService, 177
prototype, 59
WindowsPrincipal, 163
XMLHTTPRequest, 131
XMLHttpRequest object, 132, 134
OnClick event, 197
OnClientActiveTabChanged event, 195
OnHoverOut event, 197
OnHoverOver event, 197
OnLoad event, 197
OnMouseOut event, 197
OnMouseOver event, 197
OnPreRender method, 287 , 288 , 289 , 291
_onPropertyChanged method, 277


Previous Page
Next Page

Index

P

page execution lifecycle, 25
page layout
Accordion control, 186, 188
AlwaysVisibleControlExtender control, 189, 191
CollapsiblePanelExtender control, 191
ResizableControl control, 192, 194
TabContainer control, 195
TabPanel control, 195
page lifecycle
AJAX Library, 72
page requests
canceling, 37, 38
errors, 38
lifecycle, 34, 37
updates, 39, 40
pageLoad function, 72 , 168
pageLoaded event, 33 , 34
PageMethods object, 157
PageMethods.aspx code listing (6-19), 157 , 158
PageRequestManager, 33
getInstance function, 33
initializeRequest event, 33
lifecycle, 34
pageLoaded event, 33
pageUnload function, 72
panes
Accordion control, 186
AlwaysVisibleControlExtender control, 189, 191
AutoSize property, 186
CollapsiblePanelExtender control, 191
FadeTransition property, 186
FamesPerSecond, 186
header templates, 186
Height property, 186
ResizableControl control, 192, 194
SelectedIndex property, 186
TabContainer control, 195
TabPanel control, 195
TransitionDuration, 186
parameters, 76
parsing
XML request results
XMLHttpExecutor, 147
passing data
serialization, 149
passing data, WebRequest class, 140 , 142
Passport Service, 164
paths
scripts
setting, 128
Perfmon tool, 248
performance
perceived, 45
performance monitoring, 248 , 249
pop-up controls
CalendarExtender, 202
CascadingDropDownExtender, 206, 208
ConfirmButtonExtender, 203, 204
HoverMenuExtender, 204
ModalPopupExtender, 205
PopupControlExtender, 205
PopupControlExtender, 205
postback
asynchronous
error handling and, 127
disabling
temporarily, 45
postbacks
browser and, 17
PreLoadedProfileItems.aspx code listing (7-7), 177
Profile data
accessing from browser, 175, 176
defining, 171, 173
loading properties, 178, 179
preloading properties, 176, 178
Profile intrinsic, 173, 175
saving, 180, 183
Profile intrinsic
profile properties, 173, 175
profile service, 122 , 123 , 124 , 126
direct client access, 123
Profile.aspx code listing (5-13), 123 , 124
Profile.aspx code listing (7-6), 174 , 175
ProfileService element, ScriptManager
load method, 178
LoadProperties attribute, 176
ProfileService object, 177
ProgressTemplate, 28
properties
AsyncPostBackErrorMessage, 127
AsyncPostBackTimeout, 128
controls, 263, 264, 266
Culture, 114
EnablePageMethods
ScriptManager, 157
FadeTransitions, 186
InlineScript, 117
prototype, 261, 262, 263
ScriptPath, 128
UpdateMode, 20
XMLHttpRequest object, 132, 134
property
ServiceMethod, 266
ServicePath, 266
prototype object, 59
AJAX Library and, 60
closures, 60, 61, 63, 64
this reference, 60
prototype property, 261 , 262 , 263
Prototype script library, 13
prototypes, 57 , 58 , 59 , 60


Previous Page
Next Page

Index

Q

question mark (?), anonymous users, 164


Previous Page
Next Page

Index

R

raisePropertyChanged method, 266
readyStateChange event, 133
readyStateChangeHandler function, 133
redirectUrl parameter, 170 , 171
references
web services, 116, 117, 118
reflection functions, 78
registerClass function, 76 , 84
registerClass method, 280
registering custom control classes, 280
registering namespaces, controls, 259
registerInterface method, 81
registerNamespace function, 259
release scripts, 209 , 210 , 211 , 212
remove completedRequest method, WebRequest Manager, 146
Render method, 287 , 288 , 289 , 291
rendering
ScriptManager and, 104
request
initiating
send( ) method, 132
requests
HTTP requests
verbs, 135, 136
terminating
abort( ) method, 132
WindowsPrincipal object, 164
ResizableControl control, 192 , 194
ResolveScriptReference event, 129
resources
controls
disposing of, 279, 280
script resources, 46
Rico library, 13
RoundedCorners, 201
RoundedCorners.aspx code listing (8-8), 201 , 202


Previous Page
Next Page

Index

S

Sample.js code listing (5-3), 106
SaveProfileItems.aspx code listing (7-9), 181 , 183
scoping variables, 54 , 55 , 57
screen-scraping, 11
script
reducing, 69
references
adding, 105, 106
Script Debugger, 226
script tags
limiting, 46
Script.aculo.us, 13
ScriptIgnore attribute, 159
ScriptManager, 103
AsyncPostBackErrorMessage property, 127
AsyncPostBackTimout property, 128
CultureInfo object, 114
custom script files, 211
debug scripts, 210
EnablePageMethods property, 157
page execution lifecycle, 25
ProfileService element
load method, 178
LoadProperties attribute, 176
release scripts, 210
rendering and, 104
ResolveScriptReferece event, 129
ScriptMode and, 107
ScriptMode property, 210
ScriptPath property, 128
Scripts property, 211
shared scripts and, 253
version paths and, 253
ScriptManager control, 129
ScriptManager element, 105
ScriptManagerProxy control, 129
ScriptMethod attribute, 157
ScriptMode
Debug, 107
Inherit, 107
Release, 107
ScriptManager and, 107
ScriptReferences and, 107
ScriptMode property, ScriptManager, 210
ScriptPath property, 128
ScriptProxy.aspx code listing (6-18), 155 , 157
ScriptReference
custom scripts and, 211
ScriptReference.aspx code listing (5-2), 105
ScriptReferences
ScriptMode and, 107
ScriptReferences element, 105
ScriptResources
embedding, 108, 109, 111
scripts
compression, 129
dynamic scripts, 251, 252
static scripts, 252
globalization, 114
localization, 111, 113, 114
path
setting, 128
resources, 46
server-side controls, 284, 285
shared, 253
ScriptService attribute, 117
security
XMLHttpRequest, 132
SelectedIndex property, 186
serialization, 149
bypassing, 158, 160
custom
JavaScriptSerializer, 160
JSON, 153, 155
JSON and, 149
XML and, 149
Serialize method, JavaScriptConverter, 160
server-side controls, 283
control classes, 285, 287
OnPreRender method, 287, 288, 289, 291
registering in ASP.NET page, 291, 292
Render method, 287, 288, 289, 291
scripts
embedding, 284, 285
Servertime.asmx code listing (5-8), 116
ServiceMethod property, 266
ServicePath property, 266
ServiceReference.aspx code listing (5-9), 117
Session state, 174
web farm deployment and, 240, 241
set timeout, WebRequest, 137
SetDefaultTimeout.aspx code listing (6-12), 144 , 145
SetVerb.aspx code listing (6-5), 136
set_data method, 273
shared scripts, 253
SingleTimer.aspx code listing (2-7), 32 , 33
SOAP
services
invoking methods as, 155
SOAP (Simple Object Access Protocol), 11
src attribute, 44
SSL encryption
Authentication service and, 167
statements
eval, 65
static scripts
compression, 252
Status.aspx code listing (7-3), 169
StrangeCaseIt method, 59
string class
trim method, 87
strings, 9 , 47
accessing parts, 67
Submit button
disabling, 45
substr function, 68
substring function, 68
substring method, 68
SupportedTypes method, JavaScriptConverter, 160
syntax errors
detecting, 50
Sys.Debug class, 215
assert operations, 220
trace operations, 216, 218
Sys.Debug.assert function, 220
Sys.Debug.clearTrace function, 218
Sys.Debug.fail function, 224
Sys.Debug.traceDump function, 218
Sys.Net.WebRequest
creating directly, 135
Sys.UI.Control class
controls and, 257
System.Graphics namespace, 73
System.Web namespace, 73
system.web.extensions
jsonSerialization, 249, 250
system.web.extensions section
profileService section, 123
System.Web.Script.Serialization.Converters, 160
System.Web.UI namespace
WebResource attribute, 109


Previous Page
Next Page

Index

T

TabContainer control, 195
TabPanel control, 195
Tabs.aspx code listing (8-5), 195 , 197
this reference
prototype object, 60
Tick event
Timer control, 32
Time.aspx code listing (6-1), 133
TimedSleep.aspx code listing (6-10), 143
Timeout.aspx code listing (6-6), 137 , 138
timeouts
WebRequest, 137
WebRequestManager
default, 143, 144
Timer control
automatic updates and, 31, 32, 33
Tick event, 32
Timer.aspx code listing (2-6), 31 , 32
toString function, 84
trace operations
Sys.Debug class, 216, 218
TraceConsole.aspx code listing (4-21), 95
traceDump function, 218
tracing
disabling
deployment and, 243, 244
triggering updates, 20 , 22 , 23
trim method
string class, 87
Trim.aspx code listing (4-14), 87 , 88
try blocks, 66
try-catch-finally pattern, 66
Type class, 256
initializeBase method, 260
registerNamespace function, 259
type definitions
JavaScript and, 58
Type.getRootnamespaces function, 74
Type.isNamespace function, 74
Type.registerNamespace function, 74
type-checking, 9
types
dynamic modification, 77
inheritance, 73


Previous Page
Next Page

Index

U

UI effects
animations, 197, 198
UpdatePanelAnimation, 199, 200
DropShadow, 200
RoundedCorners, 201
undefined
null and, 52, 54
Update method, 24
UpdateMode property, 20
UpdatePanel control, 17 , 19 , 20
cautions, 26, 27
triggering updates, 20, 22, 23
Update method, 24
UpdatePanelAnimation.aspx code listing (8-7), 199 , 200
UpdateProgress control, 27 , 28
template, 28
UpdateProgress.aspx code listing (2-5), 28
UpdateProgress.aspx.cs code listing (2-4), 27
updates
automatic
Timer control and, 31, 32, 33
page requests, 39, 40
triggering, 20, 22, 23
URLs
retrieving full, WebRequest and, 142
userContext parameter, 140
UserContext.aspx code listing (6-9), 140 , 142
users
authentication, 163, 164, 166, 167, 169, 171
ValidateUser method, 166
logged in status, 169
perceived performance, 45
performing actions in partially rendered page, 44
postback and
temporarily disabling, 45
Profile data
defining, 171, 173
loading properties, 178, 179
preloading properties, 176, 178
saving, 180, 183
Profile intrinsic, 173, 175


Previous Page
Next Page

Index

V

validateParams method, 99
ValidateUser method, 166
validation controls, 26
variables
Booleans, 47
cache, 69
declaration, 48
declaring
using without, 54
functions, 47
JavaScript, 9
member variables
camel-casing, 47
null value, 10
numbers, 47
scoping, 54, 55, 57
strings, 47
types, 47, 48, 49
runtime creation, 48
static type safety, 48
verbs, HTTP requests, 135 , 136
Visual Studio
Intellisense, 92
Visual Studio .NET 2005
Internet Explorer
debugging and, 222, 224, 225, 226


Previous Page
Next Page

Index

W

Web Deployment Project, 245 , 248
Web Development Helper
messages, 234, 236
web farm deployment
MachineKey and, 240
session state and, 240, 241
Web Resource handler, 46
web services, 11
custom controls and, 277, 278
references, 116, 117, 118
WebMethod attribute, 157
WebRequest
global handling, 145, 147
URL, retrieving full, 142
WebRequest class
calls, making, 134, 135, 137, 138, 139, 140, 142
HTTP headers, custom, 138, 139, 140
HTTP verb and, 135
passing data, 140, 142
set timeout, 137
WebRequest object
XMLHttpRequest object and, 132
WebRequestExecutor, 147
WebRequestManager
add completedRequest method, 146
event handlers
registering for each request called, 147
executeRequest method, 146
remove completedRequest method, 146
timeout
default, 143, 144
XMLHttpExecutor, 147
WindowsPrincipal object, 163
authentication mode and, 164


Previous Page
Next Page

Index

X

XML
documents
comments, 92
parsing request results
XMLHttpExecutor, 147
serialization and, 149
XMLHttpExecutor, 147
XMLHttpExecutor.aspx code listing (6-15), 148
XMLHTTPRequest object, 131
methods, 132, 134
objects, 132, 134
properties, 132, 134
security, 132
XMLTime.aspx code listing (6-14), 147


List of Sidebars

Chapter 1: Developing Next-Generation Web Applications

JavaScript Libraries and AJAX
Installing ASP.NET AJAX

Chapter 3: JavaScript for the ASP.NET Developer

Naming and Casing in JavaScript

Chapter 7: ASP.NET AJAX Application Services

Security Concerns about Client-side Profile Usage